2017-07-19 9 views
0

でツリービューではない再レンダリング兄弟を行います。各ノードにはテキストフィールドがあります。は私が反応しscalajsでシンプルなツリービューを構築しましたscalajs反応し

は私が子供1.1にいくつかのテキストを書き込む:そのすべての子を持つノード1が再レンダリングされるので、私は1の下に新しい子1.2を追加した場合、今

enter image description here

、テキストが消える:

enter image description here

このJavascript-Reduxに子を追加する場合TreeViewでは、兄弟は再レンダリングされません。 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() 

答えて

2

これはそれを行う方法の線に沿ってより多くのです:

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) 
} 

変更

  • 同じの新しいインスタンスを作成し、ノードごとに新しいコンポーネントを作成しないでください成分。他の理由の中で、それはトラックに、エディタの内容に反応して、入力に
  • 追加値とのonChangeステートフルコンポーネントの状態を失って、常にそれは別の何かを持っていると思うし、それを再描画します反応して、他のエディタの変更はのみ動作しますが、それらを拭くます反応するように見えますそれが好きなときはいつでも。
  • コールバックに.runNow()を呼び出してはいけない - それは、フリンジの状況のエスケープハッチだといつもやる

を避けるべきです。これは、ステートフルなコンポーネントを使用しているため、大きな変更可能変数の上位バージョンであるため危険です。あなたは、Reactが間違った呼び出しを行い、特定の状況で実行時にあなたの状態を放棄することがあるかもしれません。ステートレスに安全な方法で同じ目標を達成することができます。その方法の1つとしてhttps://japgolly.github.io/scalajs-react/#examples/state-snapshotをチェックしてください。希望が役立ちます。

+0

あなたの助けをありがとうございました! app.TreeView.NodeBackend.renderに:(app.Nodeノード)を養うために何を知ってはいけない:15: 残念ながら、私は 'TreeView.scalaを取得します。 [エラー] .renderBackend [NodeBackend] 'あなたのコードを実行しているとき。 – Tamriel

+0

次のように私はあなたのStateSnapshotの例を理解する: すべてのコンポーネントがグローバルStateSnapshotオブジェクトへの参照を取得します。コンポーネントが新しい状態に設定すると、他のすべてのコンポーネントもその変更を取得します。 各変数にはグローバルなStateSnapshotが存在するため、変更が発生するとその変数を使用するコンポーネントのみが更新されます。 – Tamriel

+0

私はStateSnapshotsでTreeViewを実装しようとしましたが、どのように子を扱うべきかわかりません。 'Traversal.fromTraverse [Vector、Node]'は役に立たないようです。私のデータがこの形式で来るので、 'Map [Id、Node]'という形式のグローバル状態オブジェクトを使いたいと思います。 – Tamriel

関連する問題