36
votes

I am working on a react/relay-based content management system. Users can create and modify articles, which are stored on the server. I was wondering what the best way is, to handle the modified state of the article before it is saved to the server. I can think of several different ways to solve this:

1) Uncontrolled inputs

I can populate the input-elements using defaultValue and not store the state anywhere explicitly. The DOM would be used as my store for the modified data. Once the user hits "save", I collect all fields, read the values and create the mutation.

Pro:

  • No local state handling

Contra:

  • I can't really know which fields were changed and would need to send all data via the mutation. Or would need some additional logic to create a diff
  • It is not possible to update other parts of the view in response to the state changes

2) Copy in the local state:

I could keep the modified article in the local state of the React component and use controlled input fields to keep it synced.

Pro:

  • The local state could only have changed fields, so a diff would be easy
  • Other parts of the UI can respond to the changes of the local state

Contra:

  • This seems to be kind of an anti-pattern because data in the view is not coming directly from the relay. Syncing between local state and relay props might be a source for bugs

3) Server is the new local:

Simply create a mutation for every single change that is made. Using optimistic updates this should also provide a good UX.

Pro:

  • Relay is the only source of truth for data
  • The state is saved server-side, so there is the backup if the user accidentally closes the browser

Contra:

  • This would need a more complex implementation on the server-side to handle cases where the user wants to discard a draft, etc.
  • Many mutations triggered

These are three ways to solve this I could think of, but perhaps there are even better ways to solve this.

I have seen that there is a lot of discussion going on, about how to handle local state with Relay, and there might come a built-in solution with a future version of Relay, but I need a solution that works with the current version of a relay.

2
You'll probably find more takers for this sort of thing on one of the other exchanges, maybe softwareengineering.stackexchange.com. SO tends to deal with more concrete programming questions.pvg
I agree with @pvg, but this question would also be quite opinion-based, which is also off-topic. However, I will say that I personally would definitely would go with (2). You would fetch the articles and then store them in a state whenever the edit-mode comes on. This would obviously overwrite any changes to that record that happen during that time, but you can easily add some version checking to it either on the front-end or back-end. The "sync" you talk about would happen on the initial fetch and would most likely not mutate before the edits are sent back in. Have I understood this right?Chris
Seems support for local state should be on it's way by now github.com/facebook/relay/issues/114 but don't see any mention in recent blog post facebook.github.io/react/blog/2016/08/05/…vladexologija
For the moment I'm using #2 but I'm trying to move state handling to a HoC to avoid state handling in Relay components. Redux is one of my options but I'm also looking if something simpler exists. The caveat is not too forget to put your client state under the QueryRenderer to avoid resending queries when changing "local" state.hisa_py
I implement a cms with option 3. I think this is how Relay is meant (opinion based).Stefan van de Vooren

2 Answers

1
votes

Short answer: It depends, I prefer #2 solution

1. Why I prefer #2

Solution #2 is an advanced version of solution #1, so #1 is passed. But #1 of course has it's case, most old sites do that.

Solution #3 will keep the server busy, that will either raise your cost for server or slow down user experience when your business scales, so #3 is passed. But as an internal system or some business meant to be used by less user, #3 become good choice.

If the user accidentally closes the browser

Solution #2 still can use an interval to sync it's content to server, surely you've seen auto save in xx seconds many times. Or you can use localstorage, it will not save cross computer, but it will save every bit of your change, even every history with zero sync lag.

2. It is not anti-pattern

Syncing between local state and relay props might be a source for bugs

I should say this is not only happen for #2, if some other change the article at the same time

For #1, your modification will be overwritten, and you can never find a clue where your modifications gone

For #3, your content is changing all the time, can you still work on it?

So if there is such case, you need to add version control logic right? If no such case, your local state is single truth.

0
votes

Correct me if I'm wrong but I believe you're conflating two problems: you're discussing the problem of where to store local state, but you're actually concerned with the problem of conflicting changes being made by two people (e.g. another person edits the same article the current user is editing). The reason I think this is because you're talking about the Relay props changing which would only be happening if your app is actively fetching updates from the server, and there are such updates to fetch.

This seems to be kind of an anti-pattern, because data in the view is not coming directly from relay. Syncing between local state and relay props might be a source for bugs

A Relay application is not constantly syncing with the server unless you make it do so. The Relay query, and the props that get passed to the component aren't constantly updating. If there is potential for conflict you have to solve that problem, and that isn't a Relay problem, and where you store local state is not going to solve that problem.

The simplest way to handle conflicts would be:

  • Use option #2 for local state
  • Give every article an auto-incrementing version # that you fetch when you download the article for editing
  • Require article edit mutation to specify the base version # it is operating on. The server should reject any mutation that has a base that isn't the current version. Tell the user there are conflicting changes, they aren't operating on the latest version, and you can't allow them to save.

FWIW I've worked on complex Relay applications and we've always leaned on #2. Local React state is great and controlled inputs are great. It is not an anti-pattern, Relay or otherwise.