Ad

React Component Could Not Sync Props Which Created By Dynamic ReactDOM.render

- 1 answer

When I use React+Redux+Immutable, I get an issue: the component created by dynamic way, when the props change, component not rerender. Is it React bug?

I deleted business code, just React code here: http://codepen.io/anon/pen/GoMOEZ

or below:

import React from 'react'
import ReactDOM from 'react-dom'

class A extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: 'tom'
        }
    }

    dynamic() {
        ReactDOM.render(<B name={this.state.name} changeName={this.changeName.bind(this)} type={false}/>, document.getElementById('box'))
    }

    changeName() {
        this.setState({
            name: 'tom->' + Date.now()
        });
    }

    render() {
        return <div>
            top name: {this.state.name}
            <B name={this.state.name} changeName={this.changeName.bind(this)} type={true}/>
            <div id="box"></div>
            <button onClick={this.dynamic.bind(this)}>dynamic add component</button>
        </div>
    }
}

class B extends React.Component {
    render() {
        return <div>
            {this.props.type ? '(A)as sub component' : '(B)create by ReactDOM.render'}
            - name:【{this.props.name}】
            <button onClick={this.props.changeName}>change name</button>
        </div>
    }
}

ReactDOM.render(
    <A/>,
    document.getElementById('example')
);
Ad

Answer

It is not a bug, it is just not React-way to do what you want. Each call to A.render will overwrite <div id="box">...</div> deleting elements added by A.dynamic.

More idiomatic way is to add some flag, set it in onClick handler and use it in A.render to decide if <div id="box"> should be empty or not.

See edited code on codepen: http://codepen.io/anon/pen/obGodN

Relevant parts are here:

class A extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        name: 'tom',
        y?-->
class A extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        name: 'tom',
        showB: false // new flag
      }
    }

    changeName() {
        this.setState({
          name: 'tom->' + Date.now()
        });
    }

    // changing flag on button click
    showB() {
        this.setState({showB: true})
    }

    render() {
        // `dynamic` will contain component B after button is clicked 
        var dynamic;
        if(this.state.showB) {
          dynamic = <B
            name = {this.state.name}
            changeName = {this.changeName.bind(this)}
            type = {false} />
        }
        return <div>
          top name: {this.state.name}
          <B name = {this.state.name}
             changeName = {this.changeName.bind(this)}
             type = {true}/>
          <div>{dynamic}</div>
          <button onClick = {this.showB.bind(this)}>
            dynamic add component
          </button>
        </div>
      }
    }

Update
You can still use your approach, but you need to manually update dynamically created component.

E.g. you can manually rerender it in changeName function.

  changeName() {
    this.setState({
      name: 'tom->' + Date.now()
    }, this.dynamic.bind(this));
  }

Note, that this.dynamic is passed as a second argument to this.setState this ensures that it will be called when state is really updated. Just adding this.dynamic() after this.setState({...}) will use not-yet-updated state.

Codepen here: http://codepen.io/anon/pen/EPwovV

Ad
source: stackoverflow.com
Ad