0

I have a React component that's created dynamically, with data loaded into its state. This state is being cleared between click events, resulting in the visible info being lost (undefined). Where has it gone?

const React = require('react');
const ReactDOM = require('react-dom');

class App extends React.Component {

  constructor(props) {
    super(props);
    this.nodesQuantity = 0;
    this.state = {roots: []};
  }

  nextId(me) {
    return me.nodesQuantity++;
  }

  componentDidMount() {
    var roots = [1,2,3].map(node => {
        let nextId = this.nextId(this);
        return <Node key={nextId} data={node} nextId={this.nextId} depth={0} god={this} />
    });
    this.setState({roots: roots});
  } 

  render() {
    return (
        <ul>{this.state.roots}</ul>
    )
  }
}

class Node extends React.Component{
  constructor(props) {
    super(props);
    this.state = {data: this.props.data, childNodes: [], visible: true};
    this.toggleExpanded = this.toggleExpanded.bind(this);
  }

  toggleExpanded(event) {
    console.log(this.state.childNodes.length)
    let val={id:"asdf",label:"asdf"}

    if (this.state.childNodes.length > 0) {
        this.state.childNodes.forEach((childNode) => {
            childNode.setState({visible: !childNode.state.visible}) 
        });
    } else {        
        this.setState((oldState, props) => {
            let nextId = props.nextId(props.god);
            console.log(nextId);
            oldState.childNodes.push(<Node key={nextId} data={val} nextId={props.nextId} depth={props.depth+1} god={props.god} />);
        });     
    }       
    event.stopPropagation();
  }

  render() {
    return (
        <li onClick={this.toggleExpanded}>
            {this.state.data.id} ({this.state.data.label})
            <ul>{this.state.childNodes}</ul>
        </li>           
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
mut1na
  • 795
  • 6
  • 21

2 Answers2

2
(oldState, props) => {
    let nextId = props.nextId(props.god);
    console.log(nextId);
    oldState.childNodes.push(<Node key={nextId} data={val} nextId={props.nextId} depth={props.depth+1} god={props.god} />);
                      }

doesn't return anything... possibly the source of your undefined. You're also modifying previous state via push which is forbidden in the react docs:

prevState is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from prevState and props.

You ought to be doing something like:

return {childNodes:[...(oldState.childNodes)]}
Joshua R.
  • 2,282
  • 1
  • 18
  • 21
  • yes, that's definitely cleaner. But I'm still having the problem of the state being lost. Have a look here (https://jsfiddle.net/n3ygz7uk/1/) when clicking the second time on a list item to hide it – mut1na Oct 27 '17 at 09:32
  • ironically, when i click on the childNode, the `state` is populated correctly. It seems that just externally it's not visible / accessible? – mut1na Oct 27 '17 at 09:41
1

I solved it. My main problem was a misunderstanding of react lifecycle.

This answer, albeit a bit outdated, was quite useful in understanding my problem.

Also a bit switch was to store the Node as json and only build the nodes during render step. Updates are triggered with setState which pass through the componentWillReceiveProps method that updates the state of the node json and proceeds to render

Here's my solution: https://jsfiddle.net/n3ygz7uk/2/

mut1na
  • 795
  • 6
  • 21