reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.82k stars 15.27k forks source link

Parent component gets updated Redux state first, but needs updated refs coming from children. Best practices suggested? #3607

Closed flaviolivolsi closed 4 years ago

flaviolivolsi commented 4 years ago

I have a container populated dynamically with elements based on an array in the Redux store. A code example follows.

const state = {
  elements: [
    {
      id: "element_1",
      attrs: {
        width: 200,
        height: 100,
        backgroundColor: "#ff0000"
      }
    },
    {
      id: "element_2",
      attrs: {
        width: 50,
        height: 300,
        backgroundColor: "#00ffff"
      }
    }
  ]
};

// Elements Container (elements coming from Redux)
const ElementsContainer = ({ elements, elementsRefs }) => (
  <>
    {elements.map(element => (
      <div
        key={element.id}
        ref={el => {
          elementsRefs.current[element.id] = el;
        }}
      />
    ))}
  </>
);

// Parent container (elements coming from Redux)
const ParentContainer = ({ elements }) => {
  const elementsRefs = React.useRef({});

  React.useEffect(() => {
    // ...some code to animate the elements through the refs
  }, [elements]);

  return (
    <>
      <ElementsContainer elementsRefs={elementsRefs} />
    </>
  );
};

The issue is that Redux dispatches the updated state first to the parent, then to the child (ElementsContainer). Whenever I add an element to the state, the refs are assigned only after useEffect (which is meant to trigger some DOM animations with the GSAP library leveraging the ref) has been executed, meaning that useEffect cannot find the ref yet.

So far this is how I solved:

  1. I added an early return inside useEffect in case the ref doesn't exist
  2. I created a state in the parent that keeps count of the elements manipulation
  3. I pass down the setter of the state to the element, triggering it inside the ref callback
  4. I added the state that keeps count as a dependency of useEffect

All of this causes two renders for each manipulation of the elements list. I really dislike this solution and I wonder whether you can suggest a better pattern.

I made a PoC of the app using Context instead of Redux and it works as intended (here you can find the fiddle), but I still would like to hear suggestions.

markerikson commented 4 years ago

Three observations: