Friday, 14 October 2022

How to pass the set[State] function to a non-descendent trigger component

Here is the diagram. ChildComponentB has a state - stateX. In ChildComponentA, once the event occurs, it will change the stateX in ChildComponentB.

If the ChildComponentB is the child component of ChildComponentA, then it's easy, just pass the setStateX as a prop to ChildComponentA. But in this case, it's not.

a brief description diagram

The real scenario is the following. I have a canvas component, there are some static Rectangles already there, once there are mouse move over the line of the Rectangles, I'd like to add the indicator lines to another child component of the canvas component.

Hence, the rectComponent is not the descendent of the distanceIndicatorsComponent. So I can't pass the setLines to RectComponent.

What's your approach to do that?

enter image description here

If I use useContext approach, will it work?

Thank you, @KonradLinkowski to provide your solution. Here is his code. However, useContext is still lifing the state up to ParentComponent.

import React, { useContext, createContext, useState } from "react";

const Context = createContext();

function ChildComponentA(props) {
  const { setStateX } = useContext(Context);

  return (
    <div>
      componentA button:{" "}
      <button onClick={() => setStateX((i) => i + 1)}>add</button>
    </div>
  );
}

function ChildComponentB(props) {
  const { stateX } = useContext(Context);

  return <div> stateX is {stateX} </div>;
}

export default function ParentComponent(props) {
  const [stateX, setStateX] = useState(0);
  return (
    <>
      <Context.Provider value=>
        <ChildComponentA> </ChildComponentA>
        <ChildComponentB> </ChildComponentB>
      </Context.Provider>
    </>
  );
}

Regarding the reusbility of the ComponentB i.e. distanceIndicatorsComponent in this scenario, it includes the JSX and the states plus the interface in which there are logic to change the states. The are all parts which should be reusable in the furture.

From OOP perspective, the lines (state) belongs to DistanceIndicatorsComponent, and the how to change the lines (Add Line in this case) should be also reusable logic which belongs to distanceIndicatorsComponent.

However, from React perspective, to lift the setLines (this is the interface triggered under some event) is not "good enough" from OOP perspective. To lift the state - lines and state management function - setLines up to CanvasComponent is a "not good enough" in terms of the encapsulation. Put a wrap component on top of ComponentB is the same thing, the setLines still can't be passed to FrameComponent unless FrameComponent is a child-component of the wrap component.

It's very common to see there is a very heavy component holding all the state and the events at the top. It makes me feel that's a bad small of the code. The reusability of the component should be based on a set of components, in this set of components, there is one uncontrolled component at the top, and underneath of this uncontrolled component are controlled components. This set of components is a external reusability unit.

Here, in this diagram, there should be more than one reusable unit rather than one. If lift the state up to CanvasComponent, it makes all the components underneath are un-reusable. In some extents, you still can re-use the JSX of this component, but I'd say, in terms of reusablity, it should invovle as many reusable logic as possible.

I might be wrong, please correct me. And thank you for sharing your valueable comments.

Thanks for voting to close this question. But I don't know the rationality behind this.

To me, this question is not answered very well.



from How to pass the set[State] function to a non-descendent trigger component

No comments:

Post a Comment