How to manage complex list of items in react?
Asked Answered
M

4

7

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.

Milks answered 17/2, 2023 at 15:56 Comment(5)
Lifting the state up the to parent is almost certainly the way to go. Re-rendering is what is supposed to happen, but it should be able to maintain the focus state and suggestions.Doff
What is your question? Your title asks about managing a complex list, and your body is talking about managing component state. Please tell us the specific problem you're trying to solve, so that we could help you better. Thank you.Phonsa
thank you @DavidL.Walsh - the problem is that if my child components continuously update state on the parent (i.e., as the user types w/ onChange event), the child component keeps re-rendering since state is passed down from parent to child. This re-rendering causes child components to lose their focus and visible suggestions. I'll try to build a sandbox to demonstrate.Milks
@DavidL.Walsh Here is the scenario...Have the child component with input as well as dropdown to manage onChange....notify the parent only on selection of dropdown element. I will try to reproduce my idea if you could provide a sandbox or something.Condonation
it seems like changing facets should affect focus / suggestions...Leshia
R
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.

Rimini answered 23/2, 2023 at 8:53 Comment(0)
X
0

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/

Xiomaraxiong answered 20/2, 2023 at 16:29 Comment(0)
E
0

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>
    </>
  );
};
 
Enyedy answered 21/2, 2023 at 7:31 Comment(0)
F
0

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://mcmap.net/q/108384/-onchange-event-using-react-js-for-drop-down

Factorial answered 26/2, 2023 at 23:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.