簡體   English   中英

React 組件在設置 state 上沒有更新

[英]React component does not update on set state

我正在嘗試根據 state 內的標志切換有條件地渲染組件。 看起來 state 正在更新,但組件未呈現。 有人可以告訴這里發生了什么。 renderTree function 更新了 state,但隨后不調用 render。

import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";

class Widget extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      nodes: build(),
      checked: [],
      expanded: [],
      isDropdownExpanded: false,
      keyword: ""
    };
  }

  onCheck = checked => {
    this.setState({ checked }, () => {
      console.log(this.state.checked);
    });
  };

  onExpand = expanded => {
    this.setState({ expanded }, () => {
      console.log(this.state.expanded);
    });
  };

  renderTree = () => {
    this.setState(
      prevState => {
        return {
          ...prevState,
          isDropdownExpanded: !prevState.isDropdownExpanded
        };
      },
      () => {
        console.log(this.state);
      }
    );
  };

  onSearchInputChange = (event, data, searchedNodes) => {
    this.setState(prevState => {
      if (prevState.keyword.trim() && !data.value.trim()) {
        return {
          expanded: [],
          keyword: data.value
        };
      }
      return {
        expanded: this.getAllValuesFromNodes(searchedNodes, true),
        keyword: data.value
      };
    });
  };

  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.keyword !== nextState.keyword) {
      return true;
    }
    if (!_.isEqual(this.state.checked, nextState.checked)) {
      return true;
    }
    if (_.isEqual(this.state.expanded, nextState.expanded)) {
      return false;
    }
    return true;
  }

  getAllValuesFromNodes = (nodes, firstLevel) => {
    if (firstLevel) {
      const values = [];
      for (let n of nodes) {
        values.push(n.value);
        if (n.children) {
          values.push(...this.getAllValuesFromNodes(n.children, false));
        }
      }
      return values;
    } else {
      const values = [];
      for (let n of nodes) {
        values.push(n.value);
        if (n.children) {
          values.push(...this.getAllValuesFromNodes(n.children, false));
        }
      }
      return values;
    }
  };

  keywordFilter = (nodes, keyword) => {
    let newNodes = [];
    for (let n of nodes) {
      if (n.children) {
        const nextNodes = this.keywordFilter(n.children, keyword);
        if (nextNodes.length > 0) {
          n.children = nextNodes;
        } else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
          n.children = nextNodes.length > 0 ? nextNodes : [];
        }
        if (
          nextNodes.length > 0 ||
          n.label.toLowerCase().includes(keyword.toLowerCase())
        ) {
          n.label = this.getHighlightText(n.label, keyword);
          newNodes.push(n);
        }
      } else {
        if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
          n.label = this.getHighlightText(n.label, keyword);
          newNodes.push(n);
        }
      }
    }
    return newNodes;
  };

  getHighlightText = (text, keyword) => {
    const startIndex = text.indexOf(keyword);
    return startIndex !== -1 ? (
      <span>
        {text.substring(0, startIndex)}
        <span style={{ color: "red" }}>
          {text.substring(startIndex, startIndex + keyword.length)}
        </span>
        {text.substring(startIndex + keyword.length)}
      </span>
    ) : (
      <span>{text}</span>
    );
  };

  render() {
    const { checked, expanded, nodes, isDropdownExpanded } = this.state;
    let searchedNodes = this.state.keyword.trim()
      ? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
      : nodes;
    return (
      <div>
        <Dropdown fluid selection options={[]} onClick={this.renderTree} />
        {isDropdownExpanded && (
          <div>
            <Input
              style={{ marginBottom: "20px" }}
              fluid
              icon="search"
              placeholder="Search"
              iconPosition="left"
              onChange={(event, data) => {
                this.onSearchInputChange(event, data, searchedNodes);
              }}
            />
            <CheckboxTree
              nodes={searchedNodes}
              checked={checked}
              expanded={expanded}
              onCheck={this.onCheck}
              onExpand={this.onExpand}
              showNodeIcon={true}
            />
          </div>
        )}
      </div>
    );
  }
}

export default Widget;


問題出在您的shouldComponentUpdate方法中:

shouldComponentUpdate(nextProps, nextState) {
    if (this.state.keyword !== nextState.keyword) {
      return true;
    }
    if (!_.isEqual(this.state.checked, nextState.checked)) {
      return true;
    }
    if (_.isEqual(this.state.expanded, nextState.expanded)) {
      return false;
    }
    return true;
  }

由於renderTree只改變isDropdownExpanded值, shouldComponentUpdate總是返回false

如果shouldComponenetUpdate返回 true,那么您的組件會重新渲染,否則不會。

在您的代碼沙箱中,可以看到每次單擊下拉列表時, shouldComponenetUpdate都會針對此條件返回 false

if (_.isEqual(this.state.expanded, nextState.expanded)) {
      return false;
    }

您需要在您的 renderTree function 中更改此變量的 state 或者您需要將此條件重寫為

 if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
      return false;
    }

Ciao,要在 React 中強制重新渲染,你必須使用shouldComponentUpdate(nextProps, nextState) function。 就像是:

shouldComponentUpdate(nextProps, nextState) {
  return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}

當您更改isDropdownExpanded值時,將觸發 shouldComponentUpdate 並且如果 return 等於 true,則將重新渲染組件。 這里的工作示例(基於您的代碼框)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM