coherence
coherence

Reputation: 93

React- How to update child prop based on parent state

I'm running into a problem getting a child react component to update when its parent stage changes. I have an Editor parent component that sets its state and then updates the state if the component receives an updated schedule (from a graphQL mutation component).

The problem is that componentDidUpdate triggers which does trigger the Modefield to update, but it is before the setState in componentDidUpdate can update the state. This means the child doesn't update. (Note- I know a more idiomatic way is to get rid of state all together, but this way allows a field to both edit and create a new one.)

How can I cause the child to update based on the parent's state change?

export const updateScheduleMutation = gql`
  mutation updateScheduleMutation(
    $id: ID!
    $mode: String
  ) {
    updateSchedule(
      id: $id
      mode: $mode
    ) {
      id
      mode
    }
  }
`;

class EditorWrapper extends React.Component {
    constructor(props) {
        super(props);
            this.state = { scheduleId: props.scheduleId || '' };
    }

    render() {
        return (
            <Mutation mutation={updateScheduleMutation}>
              {(updateSchedule, { mutationData }) => <Editor {...data} updateSchedule={updateSchedule} />}
            </Mutation>
        )
    }
}

class Editor extends React.Component {
  constructor(props) {
    super(props);
    const { schedule } = props;
    if(schedule === null){
        this.state = {
        schedule: { mode: schedule.mode || "" }
        };
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.schedule !== this.props.schedule) {
      this.setState({ ...this.props.schedule });
    }
  }

  changeInput = (path, input) => {
    const { updateSchedule, schedule } = this.props;
    const field = path.split('.')[1];
    updateSchedule({ variables: { id: schedule.id, [field]: input } });
    this.setState({ [path]: input });
  };

  render() {
    return (
        <ModeField input={this.state.schedule.input} />
    );
  }
}

const ModeField = ({input}) => FormControl value={input} />

EDIT: I updated the component to show the higher level graphQL wrapper. The reason why I wanted state in the Editor component is that in the event the graphQL query comes back as null, I set this.state.mode to an empty string, which I then update on change. Then, I would create a new schedule item on submit.

Upvotes: 3

Views: 7240

Answers (1)

Mos&#232; Raguzzini
Mos&#232; Raguzzini

Reputation: 15821

LIFT THE STATE UP! Try to manage the base state of your data in parent component and use the data as props in your component:

You also can try getDerivedStateFromProps, but before check the react blog advices:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Upvotes: 4

Related Questions