简体   繁体   English

无法从React js中深度嵌套的子级更新父级的状态

[英]Cant update state of parent from deeply nested children in React js

I'm trying to construct some SVG of a tree (a json-object) recursively using react components. 我正在尝试使用react组件递归构造树的一些SVG(一个json对象)。 Every node (a tree's node) component returns other nodes (and edges) until there are no more child-nodes. 每个节点(树的节点)组件都会返回其他节点(和边),直到不再有子节点为止。 The tree renders perfectly on my screen. 这棵树完美地呈现在我的屏幕上。

Now I want to count all the leafe-nodes using a function wich I pass down via props to all the nodes. 现在,我想使用通过道具传递到所有节点的函数来计算所有叶节点。 When a node got no child-nodes, it calls the function in its constructor. 当一个节点没有子节点时,它将在其构造函数中调用该函数。 With this function I want to increase this.state.countLeafs of my main component using this.setState. 通过此功能,我想使用this.setState增加我的主要组件的this.state.countLeafs。 The function got called as often as there are leafe nodes in my tree (I'm checking it with console.log). 该函数被调用的次数与我的树中有叶子节点的次数相同(我正在使用console.log对其进行检查)。 For every leaf node the function got called, but my state does not update. 对于每个叶节点,都会调用该函数,但是我的状态不会更新。

my main component: 我的主要组成部分:

import React, { Component } from 'react';
import SpecNode from './graphicTreeComponents/SpecNode.js';

class GraphicController extends Component {
    constructor(props){
        super(props);
        this.incLeafs = this.incLeafs.bind(this);

        this.state = ({
            u: {key: "0", x: 100, y: 0, k: 
                    [
                    {key: "1", x: 40, y: 100, k: null},
                    {key: "2", x: 160, y: 100, k: null}
                    ]
                },
            countLeafs: 0
        })
    }
    incLeafs(){
        console.log("leafs++");
        this.setState({countLeafs: this.state.countLeafs + 1});
        console.log(this.state.countLeafs);
    }
    render(props){
        return (
            <div className="GraphicController">
                <SpecNode {...this.state.u} incLeafs={this.incLeafs}/>
            </div>
        );
    }
}
export default GraphicController;

and my node component: 和我的节点组件:

import React, { Component } from 'react';
import Edge from './Edge.js';
import SpecNode2 from './SpecNode.js';

class SpecNode extends Component{

    constructor(props){
        super(props);

        if(!this.props.k){
            this.props.incLeafs();
        }
    }

    render(props){
        return(
            <svg>
                {this.props.k ? 
                    this.props.k.map(e =>
                        <svg>
                            <Edge x1={this.props.x} y1={this.props.y} x2={e.x} y2={e.y} />
                            <SpecNode2 {...e} incLeafs={this.props.incLeafs}/>
                            <circle cx={e.x} cy={e.y} r={5} fill={"lightgrey"} />
                            <text fontSize={7} x={e.x-2} y={e.y+3} fill="black">{e.key}</text>
                        </svg>
                    ) 
                    :
                    null
                }
            </svg>
        );
    }
}
export default SpecNode;

Is there anything fundamental I miss? 我想念什么基本的东西吗?

Thank you 谢谢

Are you sure the leafCount is not updating? 您确定leafCount没有更新吗? setState is asynchronous, but it takes a callback. setState是异步的,但是需要回调。 Your console.log might be happening before your state is updated. 您的console.log可能是在状态更新之前发生的。 You should try something like... 你应该尝试类似...

incLeafs(){
    console.log("leafs++");
    this.setState({countLeafs: this.state.countLeafs + 1}, () => {
        console.log(this.state.countLeafs);
    });   
}

In your main component, you don't need the parentheses when initializing state. 在您的主要组件中,初始化状态时不需要括号。

this.state = {
        u: {key: "0", x: 100, y: 0, k: 
                [
                {key: "1", x: 40, y: 100, k: null},
                {key: "2", x: 160, y: 100, k: null}
                ]
            },
        countLeafs: 0
    }

You could also try moving your function call to componentDidMount within the SpecNode component 您也可以尝试将函数调用移至SpecNode组件内的componentDidMount

constructor(props){
    super(props);
}
componentDidMount() {
    if(!this.props.k){
        this.props.incLeafs();
    }
}

Your method this.incLeafs is not bound to the parent component. 您的方法this.incLeafs未绑定到父组件。 Because of that, when it runs, this doesn't mean the GraphicController when the child node calls the method. 因此,当它运行时, this并不意味着子节点调用该方法时的GraphicController

Either 要么

A) change the way you've written the method on the parent class into an arrow function, like this: incLeafs = () => {...}; A)将您在父类上编写方法的方式更改为箭头函数,如下所示: incLeafs = () => {...};

B) manually bind the method method at some point with something like this.incLeafsHandler = this.incLeafs.bind(this) and then pass this.incLeafsHandler as the incLeaf callback to SpecNode. B)在某些时候手动将方法方法与this.incLeafsHandler = this.incLeafs.bind(this) this.incLeafsHandler ,然后将this.incLeafsHandler作为incLeaf回调传递给SpecNode。 ((I strongly don't like this solution, but that's just my preference.)) ((我强烈不喜欢这种解决方案,但这只是我的偏爱。)

C), pass it down as an arrow function in the props (as <SpecNode ... incLeafs={() => this.incLeafs()} /> ) C),将其向下传递为道具中的箭头函数(如<SpecNode ... incLeafs={() => this.incLeafs()} />

Arrow functions always retain this as whatever it was when the arrow functions were declared. 箭头函数总是保留this为不管它是什么,当箭头功能被宣布。 ( Options A and C ) (选项A和C)

Normal functions will change this based on where they're getting called, unless you use a bound copy of the original method. 除非使用原始方法的绑定副本,否则普通函数将根据调用它们的位置来this进行更改。 ( Option B ) (选项B)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM