4

I have create a very small app to demonstrate my query.

Below shown code has the functionality where the component is dynamically added to DOM using ReactDOM.render and this component carries a prop called title, but when I update the title of the parent component ( in state ) the DynamicComponent doesn't update.

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

const DynamicComponent = (props) => {
    return (
        <div style={{ 'border': '2px dotted green' }} >Dynamic Component : {props.title}</div>
    )
}

class App extends React.Component {
    state = {
        title: 'Iam Title'
    }

    addBlock = () => {
        return ReactDOM.render(<DynamicComponent title={this.state.title} />, document.getElementById('dynamiccomponents'))
    }

    render() {
        return (
            <div>
                <div>Value in state: <b>{this.state.title}</b></div>
                <p><b>&lt;DynamicComponent /&gt;</b> Added Initially</p>
                <DynamicComponent title={this.state.title} />
                <br />
                <p><b>&lt;DynamicComponent /&gt;</b> Added By ReactDOM.render will be shown below: </p>
                <div id="dynamiccomponents"></div>



                <button onClick={this.addBlock} >Click to Dynamic Component</button>
                <button onClick={() => this.setState({ title: `Update Title` })} >Update Title</button>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('root'));

The first button is used to added the DynamicComponent, works fine as expected. The Second button is used to update the title in state, now the title got changed but still DynamicComponent doesn't update.

am I missing anything, how do I solve this issue, any help would be appreciated

Thanks

vivek mengu
  • 87
  • 1
  • 7

3 Answers3

2

You could re-render the component after state change using a LifeCycle method componentDidUpdate()

    import React from "react";
import ReactDOM from "react-dom";

const DynamicComponent = props => {
  return (
    <div style={{ border: "2px dotted green" }}>
      Dynamic Component : {props.title}
    </div>
  );
};

class App extends React.Component {
  state = {
    title: "Iam Title"
  };

  addBlock = () => {
    return ReactDOM.render(
      <DynamicComponent title={this.state.title} />,
      document.getElementById("dynamiccomponents")
    );
  };
  componentDidUpdate() {
    return ReactDOM.render(
      <DynamicComponent title={this.state.title} />,
      document.getElementById("dynamiccomponents")
    );
  }

  render() {
    return (
      <div>
        <div>
          Value in state: <b>{this.state.title}</b>
        </div>
        <p>
          <b>&lt;DynamicComponent /&gt;</b> Added Initially
        </p>
        <DynamicComponent title={this.state.title} />
        <br />
        <p>
          <b>&lt;DynamicComponent /&gt;</b> Added By ReactDOM.render will be
          shown below:{" "}
        </p>
        <div id='dynamiccomponents'></div>

        <button onClick={this.addBlock}>Click to Dynamic Component</button>
        <button onClick={() => this.setState({ title: `Update Title` })}>
          Update Title
        </button>
      </div>
    );
  }
}

export default App;
Abdullah Abid
  • 1,541
  • 3
  • 10
  • 24
  • Is there any-other way without re-rendering it again, I mean if i change the props it should reflect the component right – vivek mengu Mar 04 '20 at 19:59
  • well you could have some sort of centralized state and use that in the all the components. Refrence : [https://stackoverflow.com/a/38904385/9871756] – Abdullah Abid Mar 04 '20 at 20:32
1

This is because when you call addBlock, you are only rendering <DynamicComponent title={this.state.title} /> once to the <div id="dynamiccomopnents"></div>.

When you update the state of title by clicking the button, it re-runs your App's render function, but this.addBlock does not get run again in your render function and therefore your title does not get updated. You can verify this by clicking the button that calls this.addBlock again. It will render your component again, with the updated title.

I'd suggest you introduce some state to conditionally render your component instead of using ReactDOM.render. That way, your component gets re-rendered everytime your render method is run. Here's an example:

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

const DynamicComponent = (props) => {
  return (
    <div style={{ 'border': '2px dotted green' }} >Dynamic Component : {props.title}</div>
  )
}

class App extends React.Component {
  state = {
    title: 'Iam Title',
    showBlock: false,
  }

  addBlock = () => {
    // this method now sets `this.state.showBlock` to true
    this.setState({ showBlock: true });
  }

  renderBlock = () => {
    // return any component you want here, you can introduce some conditional
    // logic or even return nested elements, for example:
    return (
      <div>
        <p>Dynamic Component!</p>
        <DynamicComponent title={this.state.title} />
      </div>
    );
  }

  render() {
    return (
      <div>
        <div>Value in state: <b>{this.state.title}</b></div>
        <p><b>&lt;DynamicComponent /&gt;</b> Added Initially</p>
        <DynamicComponent title={this.state.title} />
        <br />
        <p><b>&lt;DynamicComponent /&gt;</b> Added By ReactDOM.render will be shown below: </p>

        {/* This will run `this.renderBlock` only if `this.state.showBlock` is true */}
        {this.state.showBlock && this.renderBlock()}



        <button onClick={this.addBlock} >Click to Dynamic Component</button>
        <button onClick={() => this.setState({ title: `Update Title` })} >Update Title</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
kennyvh
  • 2,526
  • 1
  • 17
  • 26
  • Thanks @khuyuh, but DynamicComponent wont be the single component there can by any number of dynamic component above should is only to demonstrate the problem. is the any way to add specific component to a div element and should work fine with the state change – vivek mengu Mar 04 '20 at 19:23
  • in that case, i'd suggest you create a function that renders any component you want to that spot. i'll edit my answer above, let me know if this solves your case – kennyvh Mar 04 '20 at 19:25
  • sure, I will wait for your answer – vivek mengu Mar 04 '20 at 19:28
  • see the edited answer, i added a method called `renderBlock` that can return any component and render it to that spot. – kennyvh Mar 04 '20 at 19:30
  • Nope @khuyuh, this doesn't fix my issue, Actually I'am using https://bevacqua.github.io/react-dragula/ its a drag and drop plugin where the left hand side fields will be drop to right hand side while dropping I get the el ( provided by dragula events ) i use ReactDOM.render to insert my react component – vivek mengu Mar 04 '20 at 19:38
  • You should be able to replace the contents of the return in `renderBlock` to your dragula component – kennyvh Mar 04 '20 at 19:56
0

ReactDOM.render renders element only once. It creates a different tree that is not connected to your first tree. That is, React doesn't keep track of all ReactDOM.renders you might have ever called and doesn't update them with data that was used to create them

If you need to render element somewhere in the DOM tree outside of your App component but you want it to be connected with your App component (so that it reacts to state changes), use ReactDOM.createPortal

Max
  • 4,473
  • 1
  • 16
  • 18