不要 re-render Treeview 中的兄弟姐妹 scalajs-react
Don't re-render siblings in Treeview with scalajs-react
我用 scalajs-react 构建了一个简单的 TreeView。每个节点都包含一个文本字段。
我写了一些文字到 child 1.1
:
现在,如果我在 1
下面添加新的 child 1.2
,文本就会消失,因为节点 1
及其所有 children 得到 re-rendered:
在这个Javascript-ReduxTreeView中添加一个child时,兄弟姐妹不是re-rendered。我怎样才能用 scalajs-react 实现它?
请参阅下面的代码或 GitHub 上的最小示例项目。
case class Node(text: String, children: Vector[Node])
object TreeView {
val childNode = Node("1.1", Vector())
val parentNode = Node("1", Vector(childNode))
val rootNode = ScalaComponent.builder[Unit]("Node")
.initialState(parentNode)
.renderBackend[NodeBackend].build
class NodeBackend($ : BackendScope[Unit, Node]) {
def addChild =
$.modState(
_.copy(children = $.state.runNow().children :+ Node("1.2", Vector())))
def render(node: Node): VdomElement = {
val children =
if (node.children.nonEmpty)
node.children.toVdomArray(child => {
val childNode = ScalaComponent.builder[Unit]("Node")
.initialState(child)
.renderBackend[NodeBackend].build
childNode.withKey(child.text)()
})
else EmptyVdom
<.div(
node.text, <.input(), <.button("Add child", ^.onClick --> addChild),
children
)
}
}
def apply() = rootNode()
这更像是如何做到的:
case class Node(label: String, text: String, children: Vector[Node])
object TreeView {
val childNode = Node("1.1", "", Vector.empty)
val parentNode = Node("1", "", Vector(childNode))
val NodeComponent = ScalaComponent.builder[Node]("Node")
.initialStateFromProps(identity)
.renderBackend[NodeBackend]
.build
class NodeBackend($: BackendScope[Node, Node]) {
def addChild =
$.modState(s =>
s.copy(children = s.children :+ Node("1.2", "", Vector.empty)))
val onTextChange: ReactEventFromInput => Callback =
_.extract(_.target.value)(t => $.modState(_.copy(text = t)))
def render(node: Node): VdomElement = {
val children =
node.children.toVdomArray(child =>
NodeComponent.withKey(child.label)(child))
val input =
<.input.text(
^.value := node.text,
^.onChange ==> onTextChange)
<.div(
node.label, input, <.button("Add child", ^.onClick --> addChild),
children
)
}
}
def root = NodeComponent(parentNode)
}
变化
- 不要为每个节点创建新组件,而是创建同一组件的新实例。除了其他原因,React 总是认为它有一些不同并重新绘制它,丢失有状态组件的状态
- 为输入添加 value 和 onChange 以便 React 跟踪编辑器的内容,否则编辑器的更改只会看起来有效,但 React 会在需要时擦除它们。
- 不要在回调上调用 .runNow() - 这是边缘情况的逃生通道,应始终避免
行了。这仍然让我担心,因为您使用的是危险的有状态组件,因为它们是大可变变量的高级版本。在某些情况下,您可能会发现 React 进行了错误的调用并在运行时丢弃了您的状态。您可以无状态地以更安全的方式实现相同的目标,请查看 https://japgolly.github.io/scalajs-react/#examples/state-snapshot 以了解其中一种实现方式。希望对您有所帮助。
我用 scalajs-react 构建了一个简单的 TreeView。每个节点都包含一个文本字段。
我写了一些文字到 child 1.1
:
现在,如果我在 1
下面添加新的 child 1.2
,文本就会消失,因为节点 1
及其所有 children 得到 re-rendered:
在这个Javascript-ReduxTreeView中添加一个child时,兄弟姐妹不是re-rendered。我怎样才能用 scalajs-react 实现它?
请参阅下面的代码或 GitHub 上的最小示例项目。
case class Node(text: String, children: Vector[Node])
object TreeView {
val childNode = Node("1.1", Vector())
val parentNode = Node("1", Vector(childNode))
val rootNode = ScalaComponent.builder[Unit]("Node")
.initialState(parentNode)
.renderBackend[NodeBackend].build
class NodeBackend($ : BackendScope[Unit, Node]) {
def addChild =
$.modState(
_.copy(children = $.state.runNow().children :+ Node("1.2", Vector())))
def render(node: Node): VdomElement = {
val children =
if (node.children.nonEmpty)
node.children.toVdomArray(child => {
val childNode = ScalaComponent.builder[Unit]("Node")
.initialState(child)
.renderBackend[NodeBackend].build
childNode.withKey(child.text)()
})
else EmptyVdom
<.div(
node.text, <.input(), <.button("Add child", ^.onClick --> addChild),
children
)
}
}
def apply() = rootNode()
这更像是如何做到的:
case class Node(label: String, text: String, children: Vector[Node])
object TreeView {
val childNode = Node("1.1", "", Vector.empty)
val parentNode = Node("1", "", Vector(childNode))
val NodeComponent = ScalaComponent.builder[Node]("Node")
.initialStateFromProps(identity)
.renderBackend[NodeBackend]
.build
class NodeBackend($: BackendScope[Node, Node]) {
def addChild =
$.modState(s =>
s.copy(children = s.children :+ Node("1.2", "", Vector.empty)))
val onTextChange: ReactEventFromInput => Callback =
_.extract(_.target.value)(t => $.modState(_.copy(text = t)))
def render(node: Node): VdomElement = {
val children =
node.children.toVdomArray(child =>
NodeComponent.withKey(child.label)(child))
val input =
<.input.text(
^.value := node.text,
^.onChange ==> onTextChange)
<.div(
node.label, input, <.button("Add child", ^.onClick --> addChild),
children
)
}
}
def root = NodeComponent(parentNode)
}
变化
- 不要为每个节点创建新组件,而是创建同一组件的新实例。除了其他原因,React 总是认为它有一些不同并重新绘制它,丢失有状态组件的状态
- 为输入添加 value 和 onChange 以便 React 跟踪编辑器的内容,否则编辑器的更改只会看起来有效,但 React 会在需要时擦除它们。
- 不要在回调上调用 .runNow() - 这是边缘情况的逃生通道,应始终避免
行了。这仍然让我担心,因为您使用的是危险的有状态组件,因为它们是大可变变量的高级版本。在某些情况下,您可能会发现 React 进行了错误的调用并在运行时丢弃了您的状态。您可以无状态地以更安全的方式实现相同的目标,请查看 https://japgolly.github.io/scalajs-react/#examples/state-snapshot 以了解其中一种实现方式。希望对您有所帮助。