Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should state live in nested form component UI using Relay?

I have a UI that is basically a form, but it has different components for editing specific pieces of data. I want all of the data on the page to be saved at the same time, when the user clicks the save button. All the the data is contained by in a type, and I have an update mutation for that type. So, while the different children components may be editing different pieces of the object that will be saved, the parent component is going to be responsible for performing a single update mutation that saves all the data.

The problem I am running into is that I am not sure where I should have the state for the child components. No matter what, it seems that the state for them will have to be in the parent so that it has it and can include it in the input object that it passes to the mutation when it performs it. However, these leaves me in one of two places, neither which seems ideal:

  1. Only store the state of the values being edited by the child components in the parent.
    • Issue: Relay throws a warning, because I am passing a state object to a child RelayContainer, when it expects it to be a fragment fetched by Relay. You can see this warning and an example of this behavior in this playground. Here is the specific warning I am referring to that you see when editing a field in a child component.

      Warning: RelayContainer: Expected prop details supplied to [object Object] to be data fetched by Relay. This is likely an error unless you are purposely passing in mock data that conforms to the shape of this component's fragment.

  2. Let the child components maintain their own state, but then also communicate the values to the parent so that it has them for the purposes of handling the save action and performing the mutation.
    • Issue: Duplication of state. Even though the state that is in the parent is solely being kept for the purposes of performing the mutation, it seems quite wrong to have it there and will cause unnecessary renders for the parent. You can see an example of this variation in this other playground.

Hopefully there is an obvious preferred way to handle this situation, but from my searching around and playing with Relay I have not been able to determine it.

like image 891
jameson Avatar asked Apr 23 '16 07:04

jameson


1 Answers

First things first. Very well-written question. Thank you :-)

The common pattern used in React and Relay world is that we have a parent component which acts a thin view controller, fetches data using Relay and passes its state's different parts as props to different child component as necessary. The child components, which receive data as props, usually are dumb and just display passed data.

In your case, the ChildComponent itself edits data. It does receive initial details data from its parent component HelloApp. However, when we edit any of the details fields in UI, the values fetched by Relay and those in UI do not match. While creating a Relay container for child component, if we include fragments and fields, we declare that those data will be fetched by Relay. Now, if we want to modify child components' data(props) ourselves, we just skip that fragment in the Relay container. The props passed by the parent component will be used only for initialState of the child component.

The necessary changes are in HelloApp container. The fields of details are moved from child to parent.

HelloApp = Relay.createContainer(HelloApp, {
  fragments: {
    person: () => Relay.QL`
      fragment on Person {
        name,
        details {
          hairColor,
          nickName
        }
      }
    `,
  }
});

BUT I still feel uncomfortable with the above solution. Because it clearly shows that, the parent has to know the fields of details, which are actually used in child components.

like image 152
Ahmad Ferdous Avatar answered Nov 15 '22 09:11

Ahmad Ferdous