In a few projects now, I seem to face a common problem, and I still don't feel like I have created a solid way of tackling it.
I am using React, Redux, and React Router.
Current stateIn it's simplest, I have a page, with a series of saveable forms all of which can exist in both a view mode and an edit mode:
+--------------------------+
| EDIT MODE |
+--------------------------+
| |
| <input name="A"/> |
| <input name="B"/> |
| |
| <button label="save"/> |
+--------------------------+
| |
| <input name="C"/> |
| <input name="D"/> |
| |
| <button label="save"/> |
+--------------------------+
+--------------------------+
| VIEW MODE [edit] |
+--------------------------+
| readonly A |
| readonly B |
| |
| readonly A |
| readonly B |
+--------------------------+
All the forms are wrapped up in a Request component, which takes a endpoint prop, and on mount will fetch the data. Once the request is complete it will render its children.
Sample page: url: owner/1/pets
const ownerId = props.params.id;
<Request endpoint={`api/owner/{ownerId}/pets`}>
<OwnerDetails id={ownerId} />
<Pets ownerId={ownerId} />
}}
</Request>
Reducer after request succeeds (noting already normalised):
{
"tables": {
"pets": {
"1": { "id":"1", "name":"max", "owner": 1 },
"2": { "id":"2", "name":"yella", "owner": 1 }
},
"owner": {
"1": { "id":"1", "name": "jim" }
}
}
}
Then each Form is a connected component, and knows the slice of state it needs based on a simple passed in id.
Each Form also has an onChange action, that gets passed to each form input. onChange updates a "draft" slice of state.
So when the user types in the following input while in edit mode of pet 1:
The full state looks like:
{
"tables": {
"pets": {
"1": { "id":"1", "name":"max", "owner": 1 },
"2": { "id":"2", "name":"yella", "owner": 1 }
},
"owner": {
"1": { "id":"1", "name": "jim" }
}
},
"draft": {
"pets": {
"1": { "name": "new name" },
}
}
}
Actions / Reducers
onChange => # updates the draft slice of state
save => # sends all data in draft to the server to be saved, upon being saved, the draft slice for that particular form is emptied, and table slice is updated with new data - in other words, the table slice is always the latest reflection of the server.
Connected forms will grab and merge table state with draft state to "get the full picture" when in edit mode. So if we're at the current reducer state above, and looking at pet 1, the connected form will grab all of tables.pets.1 data, then merge over the top draft.pets.1 data. In view mode, it only needs to grab the tables data (draft data is ignored in view mode).
The problemThis feels somewhat overcooked. It feels awkward to extract and merge draft and table data, even when using selectors. The problems of updating and pulling data from store slices of state are exacerbated when forms can have nested elements.
The questionWhat is a more concrete strategy of dealing with complex, multiple forms that can exist in a draft and state?
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4