Friend2000
Friend2000

Reputation: 55

Trouble with race condition in component lifecycle methods - How to get data in time?

I've tried a number of configurations to solve this issue, but basically I'd like parent component to run an async function, morph the result, and pass that as props to my child. I'm not really sure where to incorporate lifecycle methods, namely because ComponentDidMount will run, set state, and re-render the parent component just fine and a console.log of state inside the parent render method shows the state updated correctly with my fields. But the child component renders and shows an empty array in props.

Any ideas on this one?

import * as React from 'react';

interface CustomState {
    fields: any;
}

class Parent extends React.Component<{}, CustomState> {
    constructor(props) {
        super(props);

        this.state = {
            fields: [],
        };
    }

    public async componentDidMount() {
        let fields = await asyncCall();

        const viewFields = this.buildViewFields(fields);

        this.setState({
            fields: viewFields,
        });
    }

    private buildViewFields(fields) {
        const viewFields = fields.map((f) => {
            return {
                name: f.Title,
            };
        });

        return viewFields;
    }

    public render() {
        // ISSUE: this.state.fields if logged here shows viewfields
        return <ChildComponent fields={this.state.fields}></ChildComponent>;
    }
}

export default Parent;

Here's my child component:

class ChildComponent extends React.Component<Props, State> {
    constructor(props) {
        super(props);

        this.state = {
            viewFields: this.props.fields,
        };
    }

    public render() {
        console.log(this.state); // DOES NOT HAVE VIEWFIELDS
        return (
            <GrandChildComponent
                fields={this.state.viewFields}
            />
        );
    }
}

export default ChildComponent;

Upvotes: 0

Views: 524

Answers (1)

Danila
Danila

Reputation: 18526

You don't need to do this part

        this.state = {
            viewFields: this.props.fields,
        };

You can just use props.fields right away inside your ChildComponent.

What happens right now is:

  1. Parent renders, fields is empty
  2. Child renders, it sets viewFields to empty fields array inside constructor
  3. Parent fetches actual fields and calls setState with them, rerenders self and Child
  4. Child rerenders, but constructor is not invoked again.

If you want to actually setState inside Child from Parent props you need to use lifecycle method componentDidUpdate. It is rarely needed though and usually is just bad practice.

Upvotes: 1

Related Questions