skip to Main Content

I have a faceted search component that I am building. I am having trouble figuring out how and where to handle state.

enter image description here

Solution #1

A parent component manages state – uses a reducer to manage all components and pass state down

Cons: child components have a lot of complexity (autocomplete, focus state) and updating the state at the parent level causes child components to re-render. This means the child component’s focus state and suggestions do not work well.

Pros: Somewhat cleaner design, state handled in one reducer

Solution #2

Child components manage their own state (like uncontrolled components). Parent component just manages creating, deleting new complex components. Children update a ref on the parent once they know they are completed (lose focus).

Cons: managing two pieces of state and duplicate key bugs

Pros: child components work as expected


Help and Suggestions

Both solutions are difficult to implement, but so far I am having better luck with solution #2.

Are there any good examples of this out there? It seems that editable todo lists would have a similar issues.

I like the idea of solution #1 but I think I would need a way to defer updating the state.

4

Answers


  1. When your state is complex and used by multiple siblings/children, you definitely want that state to live on the common parent. Solution 2, where siblings each manage part of a complex state will become a nightmare to maintain – as you’d have to keep all those different states in mind while working one one.

    Another thing to keep in mind is the shape of the state itself. For a state consisting of multiple types, useReducer() is going to be your better option.

    For example; a complex state is :

    ... = usestate({
    "user" : {
    "name": "",
    "age" : null},
    skills: [],
    rate: 50,
    company: "default",
    reviews: [],
    });
    

    To update this state, you’ll have to write a lot of type checks and different types of spreads. Some are arrays, some int, some String, etc.

    At that point, a reducer makes a lot more sense. A reducer handles the state by means of an action and a payload. The state logic then lives in a separate file for these components.

    I think this is the way to go: https://www.aleksandrhovhannisyan.com/blog/managing-complex-state-react-usereducer/

    Login or Signup to reply.
  2. As far as I understood, you have 2 dropdowns, and based on selected options you are going to fetch some data. For this you can create a reusable Dropdown component and render them inside the current component like this:

    const CityDropDownOptions = [
      { label: "la", value: "los angeles" },
      { label: "ny", value: "new york" },
    ];
    
    const CountryDropDownOptions = [
      { label: "uk", value: "England" },
      { label: "tr", value: "Turkey" },
    ];
    const Search = () => {
      const [city, setCity] = useState(CityDropDownOptions[0]);
      const [country, setCountry] = useState(CountryDropDownOptions[0]);
    
      return (
        <>
          <Dropdown
            selected={city}
            // this is the callback we send down to the Dropdown component
            onSelectedChange={setCity}
            // you render this in the Dropdown component
            options={CityDropDownOptions}
          />
          <Dropdown
            selected={country}
            onSelectedChange={setCountry}
            options={CountryDropDownOptions}
          />
          {/* you somehow trigger the api request */}
          <button onClick={() => search(city.value, country.value)}>search</button>
        </>
      );
    };
     
    
    Login or Signup to reply.
  3. Definitely you need to manage the state separately more like option 2. Components are not meant only to be controlled or uncontrolled, you need to consider which information in it should be controlled (via props), and which information should be uncontrolled (via state).

    To identify your case, I would suggest follow the official guide thinking-in-react and tic-tac-toe to re-think about your components.

    In here I will straight jump to
    Step 4: Identify where your state should live

    The mindset is

    1. Identify every component that renders something based on that state.
    2. Find their closest common parent component—a component above them all in the hierarchy.
    3. Decide where the state should live

    It’s very obvious that your child components need the search text and option list to render or change itself while the parent component does not require these values, the parent component only require a list of subcomponents with it’s static value(chosen one).

    The real-time changes should happened inside, but whenever the value is decided it should lift up the state to parent. That is because from parent perspective, finally it should have a need to collect all values from children, if the state is not stored, then it could became a ‘ask’ from parent to children which is not a good approach. And it has benefit to implement logic with multiple children. On the other hand, the dropdown data and filter text is useless for parent, they more like kind of ‘temp state’ better stay inside component, therefore not need to lift them up.

    So from my view, I would arrange the state structure like this:

    Parent: state: [{type:'',value:''}...]

    Parent render children:

    for(int i=0; i < state.length; i++){ 
        <Child ...state[i] onValueChanged={(newValue)=> changeState(i,newValue)} />
    }
    

    Child props: {type:'',value:'',onValueChanged:(newValue)=>void}

    Child state: {data:[], filter:'', focused: false}

    The data of child should be loaded with different given type.

    Inside child component the UI is rendered with it’s own state.

    While the value is chosen, child call onValueChanged to notify the value changed to parent, so the re-rendering triggered from parent will limited.

    Whenever parent need to output the values(like push search text to backend), the state can be directly used.

    After all this arrange from component level is done, you could switch the parent state management to reducer with no difficult.

    Login or Signup to reply.
  4. Maybe it’s too straightforward,but why don’t just call a function when selecting the first dropdown and generate the second?

    When the user select first dropdown generate the second according to data submitted from the first

    An example of change function: https://stackoverflow.com/a/28868135/15038900

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search