Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change the children of a component dynamically in React

Is it a way to change the children of a component dynamically? I have a component which wraps other components, recursively walks the children, what I want is to change the text of the childrens that are string. e.g.

render() {
   const func = (node) => {
    React.Children.forEach(node.children, (childNode) => {
      if (childNode && childNode.props && typeof childNode.props.children === "object") {
        func(childNode.props);
      }
      else if (typeof childNode.props.children === "string"){
        console.log(childNode.props);
      }
    })
  }

  func(this.props);

  return <span>{ this.props.children }</span>;
}

Setting childNode.props.children === "something" inside the else is failing, children is read-only. Any idea how I can achieve what I want?

like image 961
Avraam Mavridis Avatar asked Jan 22 '17 20:01

Avraam Mavridis


Video Answer


3 Answers

React props are read only indeed as it stated in React docs. You can follow JSX approach, which always creates new element, instead of changing existing element props. At later stage element is reconciled with existing React component in the Virtual DOM.

To change props of the element via JavaScript you can clone element using React.cloneElement method. Please note, you should not render this.props.children in this case, instead render new resulting element tree:

render() {
   const func = (children) => {
    return React.Children.map(children, (childNode) => {
      if (typeof childNode === "string")
        return "something"; // cover case: <div>text<div></div></div>
      if (typeof childNode.props.children === "string")
        return React.cloneElement(childNode, [], "something");
      return React.cloneElement(childNode, [], func(childNode.props.children));
    })
  }

  return <span>{ func(this.props.children) }</span>;
}
like image 75
Max Vorobjev Avatar answered Sep 30 '22 06:09

Max Vorobjev


I'd suggest having dynamic children list in state of the component and render them.

JSFiddle

class Hello extends React.Component {

  constructor() {
    super();

    this.state = {
      children: [
        <div>test</div>,
        <div>test2</div>
      ]
    };

    setTimeout(() => {
        this.setState({
        children: [
          <div>new!</div>,
          <div>changed!</div>
        ]
      });
    }, 2000);

  }

  render() {
    return <p>{this.state.children}</p>;
  }

}

ReactDOM.render(
  <Hello />,
  document.getElementById('container')
);

You can apply this concept in your app.

like image 33
Michał Pietraszko Avatar answered Sep 30 '22 05:09

Michał Pietraszko


The solution would be to iterate over the this.props.children and to create the custom children like this.

class Parent extends React.Component {
  constructor(props) {
    super(props);    
    this.state = { no: 'need' };
  }
  
render() {
  
    let children = []; // children 
    let k = 0; // key
    this.props.children.forEach( function(child) {       
      if( "string" === typeof child.props.children ){      
        console.log(child);
        let enew = React.cloneElement( child,  child.props, "new_" + k );
        k++;        
        children.push(enew);  
      }else{
        children.push(child);  
      }
    });  
 

  return <span>{ children }</span>;
 }
}



var NewComponent = React.createClass({
  render: function() {
    return (
       <div className="awesome" style={{border: '1px solid red'}}>
        Hello World!  
      </div>
    );
  }
});


ReactDOM.render(
  <Parent>
    <NewComponent />
    <div>Hello</div>    
    <div>World</div>
  </Parent>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Based on your need you may even create new element instead of cloning the existing one.

let enew = React.createElement( 'div',  {key: k, className: 'new'}, "new" );
like image 22
prosti Avatar answered Sep 30 '22 05:09

prosti