如何在React中管理复杂的项目列表?

huangapple go评论72阅读模式
英文:

How to manage complex list of items in react?

问题

我有一个多面搜索组件正在构建中。我在弄清楚如何处理状态以及在哪处理状态方面遇到了问题。

解决方案 #1

一个父组件管理状态 - 使用reducer来管理所有组件并传递状态

缺点: 子组件具有很高的复杂性(自动完成、焦点状态),在父级水平更新状态会导致子组件重新渲染。这意味着子组件的焦点状态和建议无法正常工作。

优点: 设计相对清晰,状态在一个reducer中处理。

解决方案 #2

子组件管理自己的状态(就像不受控制的组件)。父组件只负责创建、删除新的复杂组件。子组件在完成后(失去焦点)通知父组件更新一个引用。

缺点: 管理两个状态和重复键错误。

优点: 子组件按预期工作。


帮助和建议

两种解决方案都很难实现,但到目前为止,我在解决方案 #2 上运气较好。

是否有类似的好例子?似乎可编辑的待办事项列表会有类似的问题。

我喜欢解决方案 #1 的想法,但我认为我需要一种推迟更新状态的方法。

英文:

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

如何在React中管理复杂的项目列表?

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.

答案1

得分: 3

绝对需要单独管理状态,更像是选项2。组件不仅仅是受控或不受控制的,您需要考虑其中应该由props控制的信息,以及应该由状态不受控制的信息。

要识别您的情况,我建议您参考官方指南思考React井字棋,重新思考您的组件。

在这里,我将直接跳到
第4步:确定状态应该存在的位置

思维方式是

  1. 确定每个基于该状态呈现某些内容的组件。
  2. 找到它们最接近的共同父组件 - 在层次结构中高于它们的组件。
  3. 决定状态应该存在的位置

很明显,您的子组件需要搜索文本和选项列表以进行呈现或更改,而父组件不需要这些值,父组件只需要一个带有静态值(选择项)的子组件列表。

实时更改应在内部发生,但每当值被确定时,它应该将状态提升到父级。这是因为从父级的角度来看,最终应该需要从子组件收集所有的值,如果状态没有存储,那么它可能会变成一个从父级向子级的“请求”,这不是一个好的方法。而且它有助于实现与多个子组件相关的逻辑。另一方面,下拉数据和过滤文本对于父组件是无用的,它们更像是“临时状态”,最好保留在组件内部,因此不需要将它们提升。

所以从我的角度来看,我会安排状态结构如下:

父组件:state: [{type: '', value: ''}, ...]

父组件渲染子组件:

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

子组件props:{type: '', value: '', onValueChanged: (newValue) => void}

子组件状态:{data: [], filter: '', focused: false}

子组件的data应该加载具有不同类型的给定类型。

在子组件内部,UI是根据它自己的状态呈现的。

在选择值时,子组件调用onValueChanged来通知父组件值已更改,因此来自父组件的重新呈现将受到限制。

每当父组件需要输出值(例如将搜索文本推送到后端),状态可以直接使用。

在完成了从组件级别的所有这些安排后,您可以轻松切换父组件的状态管理到使用reducer。
1: https://beta.reactjs.org/learn/thinking-in-react
2: https://beta.reactjs.org/learn/tutorial-tic-tac-toe

英文:

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:&#39;&#39;,value:&#39;&#39;}...]

Parent render children:

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

Child props: {type:&#39;&#39;,value:&#39;&#39;,onValueChanged:(newValue)=&gt;void}

Child state: {data:[], filter:&#39;&#39;, 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.

答案2

得分: 0

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 on 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: link to a blog post

英文:

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({
&quot;user&quot; : {
&quot;name&quot;: &quot;&quot;,
&quot;age&quot; : null},
skills: [],
rate: 50,
company: &quot;default&quot;,
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/

答案3

得分: 0

根据我的理解,您有两个下拉框,根据所选选项,您将获取一些数据。为此,您可以创建一个可重用的“Dropdown”组件,并像这样在当前组件内渲染它们:

const CityDropDownOptions = [
  { label: "la", value: "洛杉矶" },
  { label: "ny", value: "纽约" },
];

const CountryDropDownOptions = [
  { label: "uk", value: "英格兰" },
  { label: "tr", value: "土耳其" },
];
const Search = () => {
  const [city, setCity] = useState(CityDropDownOptions[0]);
  const [country, setCountry] = useState(CountryDropDownOptions[0]);

  return (
    <>
      <Dropdown
        selected={city}
        // 这是我们传递给Dropdown组件的回调函数
        onSelectedChange={setCity}
        // 在Dropdown组件中渲染这个选项
        options={CityDropDownOptions}
      />
      <Dropdown
        selected={country}
        onSelectedChange={setCountry}
        options={CountryDropDownOptions}
      />
      {/* 以某种方式触发API请求 */}
      <button onClick={() => search(city.value, country.value)}>搜索</button>
    </>
  );
};

请注意,我将下拉框选项的“label”和“value”值进行了翻译。

英文:

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: &quot;la&quot;, value: &quot;los angeles&quot; },
  { label: &quot;ny&quot;, value: &quot;new york&quot; },
];

const CountryDropDownOptions = [
  { label: &quot;uk&quot;, value: &quot;England&quot; },
  { label: &quot;tr&quot;, value: &quot;Turkey&quot; },
];
const Search = () =&gt; {
  const [city, setCity] = useState(CityDropDownOptions[0]);
  const [country, setCountry] = useState(CountryDropDownOptions[0]);

  return (
    &lt;&gt;
      &lt;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}
      /&gt;
      &lt;Dropdown
        selected={country}
        onSelectedChange={setCountry}
        options={CountryDropDownOptions}
      /&gt;
      {/* you somehow trigger the api request */}
      &lt;button onClick={() =&gt; search(city.value, country.value)}&gt;search&lt;/button&gt;
    &lt;/&gt;
  );
};

答案4

得分: 0

或许这有点直接,但为什么不在选择第一个下拉框时调用一个函数来生成第二个呢?

当用户选择第一个下拉框时,根据从第一个下拉框提交的数据生成第二个。

一个更改函数的示例:https://stackoverflow.com/a/28868135/15038900

英文:

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

huangapple
  • 本文由 发表于 2023年2月17日 23:56:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75486539.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定