【问题标题】:In React, how to handle state containing arrays?在 React 中,如何处理包含状态的数组?
【发布时间】:2021-01-14 08:49:17
【问题描述】:

我是 React 的新手,所以我很感激任何关于如何使它更符合习惯的建议。 这是一个玩具示例,但在实际示例中,我的状态包含一个实体数组。对此实体数组的任何更改都应触发重新渲染。任何用户输入或其他逻辑都可能导致一次添加或删除多个实体。

我目前通过复制实体列表、附加新项目和设置状态来将实体添加到状态。这也会触发重新渲染,这正是我想要的:

    let nextEntities = Object.assign([], this.state.entities) as boolean[];
    nextEntities.push(val);
    this.setState({
      entities: nextEntities
    });

但是,如果我连续多次执行此操作,则只会添加最后一个实体,因为 this.state.entities 不是最新的。

我什至尝试创建要添加的实体队列,并从setState 的回调中触发额外的setState 调用,但这达到了堆栈帧限制。 我知道我可以在setTimeout 回调中隔开多个添加,但这似乎很愚蠢。 钩子的一些用法是答案吗?

这是我的最小示例:https://codesandbox.io/s/headless-darkness-w1wt1?file=/src/index.tsx:0-1375

import React from "react";
import ReactDOM from "react-dom";

//My React component state has an array of entities in it
interface State {
  entities: boolean[];
}
interface Props {}

export class App extends React.Component<Props, State> {
  constructor(props) {
    super(props);
    this.state = {
      entities: [true, false]
    };
  }

  onClick = () => {
    console.log("onclick! current state:", this.state.entities);
    //based on user input,
    //  multiple items may get added to the array per frame
    this.addToStateList(true);
    this.addToStateList(false);
  };

  addToStateList(val: boolean) {
    let nextEntities = Object.assign([], this.state.entities) as boolean[];
    //TODO: in a non-toy example, we'd do some extra side-effects on each add here.
    nextEntities.push(val);
    this.setState({
      entities: nextEntities
    });
    console.log("just tried to push new value:", val);
  }

  render() {
    let lItems = this.state.entities.map((val: boolean, index: number) => (
      <div style={{ display: "inline" }} key={index}>
        {(val ? "true" : "false") + ", "}
      </div>
    ));
    return (
      <div onClick={this.onClick}>click to test. Current value: {lItems}</div>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  rootElement
);

我确定您要提供的一个潜在建议是批量添加要添加的实体,以便它们都在一个 setState 调用中添加。在我的真实案例中,这很尴尬,因为:

  • 实体在代码中的不同位置添加或删除,因此将它们批处理在一起并不容易
  • 在添加时,我需要将要添加的实体与所有预先存在的实体(如碰撞检测)进行比较。比较(要添加的实体)与(状态中的预先存在的实体 + 要添加的其他实体,除了当前正在添加的实体)只是比较麻烦。

【问题讨论】:

    标签: reactjs typescript react-hooks state


    【解决方案1】:

    因为 setState 是一个异步操作...一种可能的解决方案是使用 async-await 。我不确定这是否是最好的方法。但它肯定能完成工作。

    onClick = async () => {
        await this.addToStateList(true);
        await this.addToStateList(false);
      };
    

    【讨论】:

    • 在我的代码中添加 async/await 对性能的影响有多大?我想我真的只是想要一个同步状态更新操作。
    • 我认为应该没有太大区别
    【解决方案2】:

    试试这个。

    this.setState({
      entities: [...this.state.entities, val]
    });
    

    语法称为spread operator,如果您不熟悉的话。

    avoid mutating objects/arrays when working with React 是一个很好的经验法则。因此,如果您的应用变得复杂,有一个非常方便的库,称为 immutability-helper

    【讨论】:

    • 我看到spread算子比复制列表要简单,但是如果我一个接一个地添加val1val2,我相信仍然只会出现val2的添加,除非我按照@Mohammad Faisal 的回答建议使用 async-await。
    • 您始终可以使用setStatesecond argument。这是一个在状态更新时触发的回调,就像这样。 this.setState({entities: [...this.state.entities, val]}, () =&gt; {/* here, you're guaranteed to have the updated state */})
    • 不幸的是我试过了,但如果我尝试一次添加超过 4 个项目,我会遇到堆栈帧限制。我希望这很容易。
    猜你喜欢
    • 2019-09-23
    • 2015-02-19
    • 2021-02-16
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 2017-08-09
    • 2015-08-08
    相关资源
    最近更新 更多