When storyboard class component search example is converted to functional (hooks) errors/warnings are thrown.
import React, { Component } from "react"
import SortableTree from "react-sortable-tree"
import "react-sortable-tree/style.css"
const data = [
{
title: "Windows 10",
subtitle: "running",
children: [
{
title: "Ubuntu 12",
subtitle: "halted",
children: [
{
title: "Debian",
subtitle: "gone"
}
]
},
{
title: "Centos 8",
subtitle: "hardening"
},
{
title: "Suse",
subtitle: "license"
}
]
}
]
const nodeInfo = row => console.log(row)
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
searchString: "",
searchFocusIndex: 0,
searchFoundCount: null,
treeData: data
}
}
render() {
const { searchString, searchFocusIndex, searchFoundCount } = this.state
const customSearchMethod = ({ node, searchQuery }) =>
searchQuery &&
((node.title &&
node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
(node.subtitle &&
node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1))
const selectPrevMatch = () =>
this.setState({
searchFocusIndex:
searchFocusIndex !== null
? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
: searchFoundCount - 1
})
const selectNextMatch = () =>
this.setState({
searchFocusIndex:
searchFocusIndex !== null
? (searchFocusIndex + 1) % searchFoundCount
: 0
})
return (
<div>
<h2>Find the needle!</h2>
<form
style={{ display: "inline-block" }}
onSubmit={event => {
event.preventDefault()
}}
>
<input
id="find-box"
type="text"
placeholder="Search..."
style={{ fontSize: "1rem" }}
value={searchString}
onChange={event =>
this.setState({ searchString: event.target.value })
}
/>
<button
type="button"
disabled={!searchFoundCount}
onClick={selectPrevMatch}
>
<
</button>
<button
type="submit"
disabled={!searchFoundCount}
onClick={selectNextMatch}
>
>
</button>
<span>
{searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
/
{searchFoundCount || 0}
</span>
</form>
<div style={{ height: 300 }}>
<SortableTree
treeData={this.state.treeData}
onChange={treeData => this.setState({ treeData })}
searchMethod={customSearchMethod}
searchQuery={searchString}
searchFocusOffset={searchFocusIndex}
searchFinishCallback={matches =>
this.setState({
searchFoundCount: matches.length,
searchFocusIndex:
matches.length > 0 ? searchFocusIndex % matches.length : 0
})
}
generateNodeProps={row => {
return {
title: row.node.title,
subtitle: (
<div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
),
buttons: [
<button
type="button"
className="btn btn-outline-success"
style={{
verticalAlign: "middle"
}}
onClick={() => nodeInfo(row)}
>
ℹ
</button>
]
}
}}
/>
</div>
</div>
)
}
}

@radulle did you ever get this working with hooks? I haven't tried to convert my implementation yet.
Any news on this one?
The error message is actually just a warning and everything _seems_ to work. But it might break in future versions of React.
This is related to the following React issue:
https://github.com/facebook/react/issues/18147
Ref. comment: https://github.com/facebook/react/issues/18147#issuecomment-592267650
The problem is that the proposed solution would require components in react-sortable-tree to be converted from class components to functional ones...
I have the same issue!
Any news on this one? I have the same problem~
after going through this issue and reading this, I understood that the warning gets triggered if you want to setState a component synchronously from a different component.
So I have found a workaround to make the warning disappear, till it's fixed.
If you click on the little arrow beside Warning, you'll get a much detailed stacktrace.
For me, I have found the warning is triggered by the callback I pass to searchFinishCallback (because it's there where I change the state of my component from SortableTree component).
So, I changed my callback from something like this:
const searchFinishCallback = (matches) => {
setSearchFoundCount(matches.length)
setSearchFocusIndex((searchFocusIndex) =>
matches.length > 0 ? searchFocusIndex % matches.length : 0
)
}
To something like this:
const searchFinishCallback = (matches) => {
setImmediate(() => {
setSearchFoundCount(matches.length)
setSearchFocusIndex((searchFocusIndex) =>
matches.length > 0 ? searchFocusIndex % matches.length : 0
)
})
}
You can instead use setTimeout or anything that will make the call async.