Search code examples
reactjsfluxreactjs-flux

Sub-component not updating properly in React + Flux architecture


I think I'm not using this as I'm supposed to.

I'm trying to simplify things as much as possible, so I'll describe briefly the problem, the solutions I found and what I don't like about them.

Problem I have some flows in my app. Imagine a multi-step exercise, on each step having a list of cards. The cards and other data from one step to another are different, so basically on each step I have the same component, but with different data. Each step gets a cardsObject in getDefaultProps. In getInitialState I convert it into an array to render it more easily.

When I add a new card, the Dispatcher sends the new data to the Store. It emits a change, the View updates, it passes the new cards as props, but they don't get into the state again, so the component re-renders with the same cards.

Attaching a diagram for better understanding (the left and right columns are the same group of components. I've put them separately just to understand the flow better)

enter image description here

Solutions I've found

1. The first things that came to mind is that the component doesn't update properly. So I've set a key for it, generating a unique key every time. This means that when passing 4 cards , it goes again into the getDefaultProps and getInitialState and re-renders the components properly.

The big downside of this is that it re-renders all the component (the list of cards from it, the different texts it has). I hoped it just re-renders it in memory, but it also re-renders the DOM. So it's not performant. I'm basically erasing everything and creating it again at each update.

2. Another solution was to generate the same key for each step every time (step 1 gets key 1, and so on). In order to get the new cards, I had to get rid of getInitialState and work in render with the cards directly from props. This, I find, is the downside of this method. It means I can't work with things from the state, because only the props change.

So, I need some guidance. How do you find the 2 solutions? Can I somehow update the state when receiving the new props? ComponentWillUpdate gets triggered, and I have the new props there. How can I inject them into the state again? I've tried setState in componentWillUpdate and I got an unresponsive script. I think I need a better solution here.

Thanks in advance. I didn't post any code as it's just straightforward, nothing special.


Solution

  • Yes, you definitely want to use keys, and I think your second solution is the way it should work.

    Rendering purely from props is actually what you want as much as possible. Converting props to state is especially to be avoided. If you can derive it from props, you should just derive it props in your render function.

    In some rare cases you need to perform some heavy calculations that you don't want to repeat in every render pass. In my experience it's easy to pre-maturely optimize this: JavaScript is very fast these days. However there are cases where it might be a bottle-neck. In these cases you can use componentWillReceiveProps to convert some props to state if they changed. Note that componentWillReceiveProps does not indicate the props have actually changed values, only that they have been passed through by a parent render. It's up to you to check if the props have changed and do your heavy computation at that point and setState. Also note that componentWillReceiveProps is not called before the initial render, so you usually need some combination of componentWillMount and componentWillReceiveProps that both call into a function that computes the state. Again, if all that can be avoided by just deriving all data from props, go with that instead.