第二次单击时反应状态更新渲染

时间:2021-03-15 23:50:17

标签: reactjs typescript

我一直在挠头试图弄清楚这一点,尝试了所有我能想到的/在网上找到的东西,但没有运气。 当组件安装时,我从 API 中检索一个列表,解析它并将其推送到我所在状态的另一个数组中。我已经检查了 api 并且它工作正常,我得到了数据,在调试时我看到逻辑运行正常,我的控制台日志按预期触发。我看到的唯一问题是渲染。

我正在 ComponentDidMount 中执行以下调用:

ScheduleService.getScheduleList().then((response) =>
  response.data.scheduleList.forEach((schedule: any, index: any) =>
    this.state.nodes?.push({
      id: index,
      label: (
        <div onClick={(e) => this.handleScheduleClick(e)}>
          <Tooltip2
            content={
              "Start: " +
              schedule.startTime.utc +
              " Stop: " +
              schedule.stopTime.utc +
              " Type: " +
              schedule.scheduleType
            }
            placement="right"
          >
            {schedule.id}
          </Tooltip2>
        </div>
      ),
      isExpanded: false,
      icon: "document",
    })
  )
);

我试过在回调中添加这个调用.. 不走运。尝试将值分配给数组并随后更新状态,结果相同。

当我第一次点击路由器并进入页面时,我的树没有被填充。 Data not rendered

但在我立即切换回此页面后,我的数据呈现出来。

Data rendered

我知道每次状态改变时 React 都会更新组件,但我不明白为什么现在没有发生。

也试过运行 this.forceUpdate(),结果一样。

非常感谢您的帮助!

编辑 1: 我已经写在我正在使用的逻辑下面。

 public state: IState= {
        nodes: ITreeNode[],
    }
    
    class MyClass extends React.Component<IState> {
    
    public componentDidMount(){
       this.getScheduleList();
    }

   private getScheduleList = () => {
     ScheduleService.getScheduleList().then((response) =>
      response.data.scheduleList.forEach((schedule: any, index: any) =>
        this.state.nodes?.push({
          id: index,
          label: (
            <div onClick={(e) => this.handleScheduleClick(e)}>
              <Tooltip2
                content={
                  "Start: " +
                  schedule.startTime.utc +
                  " Stop: " +
                  schedule.stopTime.utc +
                  " Type: " +
                  schedule.scheduleType
                }
                placement="right"
              >
                {schedule.id}
              </Tooltip2>
            </div>
          ),
          isExpanded: false,
          icon: "document",
        })
      )
    );
    public render() {
        return (
<div>
         <Tree
                      contents={this.state.nodes}
                        className={
                        " focus-panelsList focus-panelsList-noborder"
                      }
                    />
                  </div>
            }
    )

2 个答案:

答案 0 :(得分:2)

首先,您不能直接编辑状态。为了更新状态和反应识别状态的变化是通过使用 this.setState 方法。 其次,您不应该在状态中存储组件。您可以在组件的 state 和 render 方法中存储所需的数据,使用 map 函数通过 nodes 并生成标签组件。 我重构了 getScheduleList 方法。 '我已经更新了它,因此它将节点设置为这个数组,而不是在每次迭代时更新状态。

private getScheduleList = () => {
  ScheduleService.getScheduleList().then((response) => {
    const nodesData = response.data.scheduleList.map((schedule: any, index: any) => ({
      id: index,
      schedule: schedule,
      label: {
        content: "Start: " +
          schedule.startTime.utc +
          " Stop: " +
          schedule.stopTime.utc +
          " Type: " +
          schedule.scheduleType,
          id: schedule.id,
      },
      isExpanded: false,
      icon: "document",
    }));
    this.setState({nodes: nodesData})
  });
}

然后你可以在你的渲染方法中生成标签组件

public renderLabels() {
  return (
    <div>
      {this.state.nodes.map(n => {
        return (
          <div key={"node-label-" + n.id} onClick={(e) => this.handleScheduleClick(e)}>
            <Tooltip2
              content={n.label.content}
              placement="right"
            >
              {n.label.id}
            </Tooltip2>
          </div>
        );
      })}
    </div>
  );
}

public render() {
  return this.renderLabels());
}

或者因为您需要在 Tree 组件的节点数组中自定义标签组件,您可以执行类似的操作。

public renderNodeLabel(node) {
  return (
    <div key={"node-label-"+node.id} onClick={(e) => this.handleScheduleClick(e)}>
      <Tooltip2
        content={node.label.content}
        placement="right"
      >
        {node.label.id}
      </Tooltip2>
    </div>
  );
}

public render() {
  return (
    <Tree
      contents={this.state.nodes.map(n => ({
        ...n,
        label: this.renderNodeLabel(n),
      }))}
      className="focus-panelsList focus-panelsList-noborder"
    />
  )
}

答案 1 :(得分:1)

问题

看来您的问题是状态突变。如果您正在加载数据并填充 ScheduleService.getScheduleList() .then((response) => { this.setState((prevState: IState) => ({ nodes: prevState.nodes.concat(response.data), })); }); 状态数组,则不应推送到当前状态。

使用功能状态更新从任何先前存在的状态进行更新,并附加新映射的空对象数组。

其次,您将 JSX 存储在 React 反模式的 state 中。您应该只存储数据,然后将状态映射到您需要传递给第三方模块的 JSX。

const data = this.state.nodes.map((schedule: any, index: any) => ({
  id: index,
  label: (
    <div onClick={this.handleScheduleClick}>
      <Tooltip2
        content={
          "Start: " +
          schedule.startTime.utc +
          " Stop: " +
          schedule.stopTime.utc +
          " Type: " +
          schedule.scheduleType
        }
        placement="right"
      >
        {schedule.id}
      </Tooltip2>
    </div>
  ),
  isExpanded: false,
  icon: "document",
}));

将状态映射到 JSX

data

this.state.nodes 传递给第三方组件,而不是您之前可能执行的 class MyClass extends React.Component<IState> { state = { nodes: [], }; public componentDidMount(){ this.getScheduleList(); }

除此之外,您也没有初始状态。您应该提供有效的初始状态。

readJson()