React component not re-rendering, although Object in props changes

3786 views javascript
-3

I know, there are many, many similary questions.. **duplicate alarm!**

But: I looked through all of them, I promise. I'm quite sure now, that this is another case, that could have to do with the props being an object (from what I've read here). But I couldn't solve the following, anyway:

class CsvListDropdown extends Component {
    constructor(props) {
        super(props);
        this.state = { sessions: props.sessions }
        this csvsInSession = this.csvsInSession.bind(this);
    }

    csvsInSession(sessions) {
        return (sessions
            .map(keys => Object.entries(keys)[2][1])
            .map((csv, i) => (
                <option value={csv} key={i}>{csv}</option>
            ))
        )
    }

    render() {
        const { isLoading } = this.props
        if (isLoading) { blablabla.. }
        else {
            return (
                ...
                <select>
                    {this.csvsInSession(this.state.sessions)}
                <select>
                ...
            )
        }
    }
}

export default withTracker(() => {
    const handle = Meteor.subscribe('sessions');
    return {
      isLoading: !handle.ready(),
      sessions: Sessions.find({}).fetch() 
    };
})(CsvListDropdown);

Now from the client I am writing another document into the Sessions collection, containing the .csv filename, while this new csv file is being uploaded to a remote server. console.log(this.props.sessions) gives me an array, which is up to date. But the component itself does not re-render.

What I also don't understand is: console.log(this.state.sessions) returns undefined. (note: state)

What I tried so far:

  • {this.csvsInSession(this.props.sessions)} (note: props)
  • Adding a withTracker / State / Props to the parent component and passing the sessions object from either state or props as params to the child component, that should re-render.

  • forceUpdate()

  • componentWillUpdate()

What may be important as well: The component should re-render about the same time another component also re-renders (which displays the contents of uploaded CSVs, that return from a microservice and get written into another collection). The latter does actually re-render.. But that dropdown does not.. argh!

answered question

Any reason you're storing sessions in state?

What does console.log(sessions) look like right as you enter the csvsInSession function?

1 Answer

13

this.state will only change if you call this.setState(), which you are not doing. You are initializing state with a value from props, but only in the constructor when the component is first instantiated. After that, even if props changes your component may re-render but what it displays won't change because state hasn't been updated.

In fact, there does not appear to be any reason whatsoever to store data in state in that component. It might as well be a functional presentational component:

function CsvListDropdown(props) {
    function csvsInSession(sessions) {
        return (sessions
            .map(keys => Object.entries(keys)[2][1])
            .map((csv, i) => (
                <option value={csv} key={i}>{csv}</option>
            ))
        )
    }

    const { isLoading } = props;
    if (isLoading) { blablabla.. }
    else {
        return (
            ...
            <select>
                {csvsInSession(props.sessions)}
            <select>
            ...
        )
    }
}

Generally all of your components should be stateless functional components unless they specifically need to store internal state for some reason.

posted this

Have an answer?

JD

Please login first before posting an answer.