美文网首页Front End
[FE] componentWillReceiveProps陷阱

[FE] componentWillReceiveProps陷阱

作者: 何幻 | 来源:发表于2017-09-12 18:06 被阅读569次

    1. 生命周期

    React父子组件体系中,首次渲染的生命周期函数,触发情况如下,

    
    Father: componentWillMount
    Father: render
        Son: componentWillMount
        Son: render
        Son: componentDidMount
    Father: componentDidMount
    

    等渲染完后,在父组件的componentDidMount中调用setState
    生命周期函数,触发情况如下,

    Father: setState
    Father: shouldComponentUpdate ---- true
    Father: componentWillUpdate
    Father: render
        Son: componentWillReceiveProps
        Son: shouldComponentUpdate ---- true
        Son: componentWillUpdate
        Son: render
        Son: componentDidUpdate
    Father: componentDidUpdate
    

    注:
    父组件或子组件的shouldComponentUpdate函数是关键,如果返回false
    会导致其后的componentWillUpdaterendercomponentDidUpdate都不执行。

    2. componentWillReceiveProps的重要性

    在实际开发过程中,如果父组件改变了子组件的属性值
    并且,子组件是通过自身state来渲染页面的话,

    就必须在子组件的componentWillReceiveProps中调用,setState(nextProps)

    componentWillReceiveProps(nextProps){
        this.setState(nextProps);
    }
    

    否则,因为子组件的state没有任何改变,
    虽然会经历shouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate
    但是因为Diff算法的原因,render函数也不会更新DOM。

    3. 子组件不更新

    下面我们来看一个真实场景的例子。

    我们有FatherSon两个组件,
    (1)父组件改变了子组件的属性值
    由于Father组件通过在自身componentDidMount中调用setState,改变了自身的state
    所以,Father组件的生命周期函数shouldComponentUpdatecomponentWillUpdaterender依次被调用,
    render中,y={this.state.x}改变了子组件的属性值。

    class Father extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                x: 1
            };
        }
    
        componentDidMount() {
            setTimeout(() => this.setState({
                x: 2
            }), 1000);
        }
    
        render() {
            return (
                <Son y={this.state.x} />
            );
        }
    }
    

    (2)子组件是通过自身state来渲染页面

    Son子组件中,使用了自身state来渲染页面,{this.state.y}
    那么,如果子组件componentWillReceiveProps不进行setState
    Father组件对Son组件属性的影响就不会改变Son组件的state

    虽然Son组件的shouldComponentUpdatecomponentWillUpdaterender都会被调用,
    但是由于state没有更新,DOM也没有更新

    class Son extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                y: props.y
            };
        }
    
        // 反模式
        // componentWillReceiveProps(nextProps){
        //     this.setState(nextProps);
        // }
    
        render() {
            return (
                <div>{this.state.y}</div>
            );
        }
    }
    

    去掉以上componentWillReceiveProps的注释,
    通过setState,子组件就更新了。

    此外,如果Son组件中,直接使用{this.props.y}来渲染,也可以避免这个问题。
    因为,这样会改变div组件的属性,导致div组件更新。

    4. 诡异的反模式

    我在实际项目中,遇到了一个子组件在被更新属性时不setState的例子,
    但是却惊奇的发现它居然可以更新DOM。

    原因是,父组件的render函数,写法很诡异
    每次render都创建一个新的组件,例如,

    render() {
        const A = () => (
            <Son y={this.state.x} />
        );
    
        return (
            <A />
        );
    }
    

    这样实际每次render的是新组件<A />
    同时Son组件也会被新建,(可验证componentWillMount又触发了)
    肯定更新DOM了。

    然而这是一个反模式,最好不要这么写。


    参考

    React.Component: componentWillReceiveProps

    相关文章

      网友评论

        本文标题:[FE] componentWillReceiveProps陷阱

        本文链接:https://www.haomeiwen.com/subject/tdthsxtx.html