【问题标题】:How to call setState method of a parent component after updating child component's state?更新子组件状态后如何调用父组件的setState方法?
【发布时间】:2018-08-10 11:08:45
【问题描述】:

我有一个包含子组件的组件。子组件有许多复选框。每个复选框的 onClick 将复选框的状态变为 true 并更新整个组件。每次点击checkbox 时,我都需要父组件中选中复选框的标签,因此我将一个函数从父组件传递给子组件。在componentDidUpdate() 中,当我调用从父级通过道具传递的函数时,它工作得非常好。目前,我得到了子组件的更新状态,即选中复选框的标签,我可以通过 console.log 看到它们。但是,如果我不使用console.log(),而是使用setState({SelectedLabels:selected}),它会进入无限循环并在片刻之后显示错误:超出最大限制。我搜索了很多。 React 文档说你不能在componentDidUpdate()、componentWillUpdate 甚至shouldComponentUpdate() 中调用setState()。有人可以告诉我在哪里可以使用它吗? 这是我的代码:

父组件:

    class BuildingsModal extends React.Component {
  state = {
    open: false,
    breadcrumbEntries: [{ title: "World" }],
    disabled: true
  };

  handleClickOpen = () => {
    this.setState({ open: true });
  };

  handleClose = () => {
    this.setState({ open: false });
  };
// This is my function that goes to child to get run after child gets updated so the received array can be used to set the disabled state of this component. If I do not call setState of this component and only console.log() function, it work fine. There is an issue with the state update. Please help!! 
  handleSelectedItems = selectedArray => {
    selectedArray.length > 0 && this.setState({disabled:false});
      console.log(
        "SelectedArray:",
        selectedArray,
        "disabled",
        this.state.disabled
      );
  };

  render() {
    const { fullScreen } = this.props;
    const { open, breadcrumbEntries, disabled } = this.state;
    return (
      <div>
        <PageHeader
          title="Dashboard"
          subtitle="33 Irving Place, New York, NY 10003"
          icon
          onClick={this.handleClickOpen}
        />
        <Dialog
          open={open}
          onClose={this.handleClose}
          scroll="body"
          fullScreen={fullScreen}
          maxWidth="sm"
          PaperProps={{
            style: {
              paddingLeft: 77.5,
              paddingRight: 77.5,
              paddingTop: 20,
              paddingBottom: 44,
              borderRadius: 23
            }
          }}
        >
          <DialogHeader>
            <Title>33 Irving Place</Title>
            <DialogActions>
              <LinkButton onClick={this.handleClose} type="danger">
                Cancel
              </LinkButton>
              <LinkButton onClick={this.handleClose} type="success">
                Save
              </LinkButton>
              <LinkButton onClick={this.handleNext} disabled={disabled}>
                Next
              </LinkButton>
            </DialogActions>
          </DialogHeader>
          <ContentWrapper>
            <Breadcrumb
              entries={breadcrumbEntries}
              getIndex={index => console.log("index is: ", index)}
            />
            <Content type="world" getSelectedItems={this.handleSelectedItems} />
          </ContentWrapper>
        </Dialog>
      </div>
    );
  }
}

export default BuildingsModal;

子组件:

class Contents extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null,
      checkedItems: new Map(),
      filteredItems: []
    };
    this.handleChange = this.handleChange.bind(this);
  }

  componentDidUpdate() {
    var filteredItems = [];
    const filterItems = (value, key, map) => {
      value === true && filteredItems.push(key);
    };
    this.state.checkedItems.forEach(filterItems);
    this.props.getSelectedItems(filteredItems);
  }

  getWorldData() {
    this.setState({
      data: [
        "United State",
        "United Kingdom",
        "China",
        "Korea",
        "Japan",
        "Pakistan",
        "India",
        "Bangladesh"
      ]
    });
  }

  getCountryData() {
    this.setState({
      data: [
        "New York",
        "Los Angeles",
        "Chicago",
        "Houston",
        "Phoenix",
        "Philadilphia",
        "Dallas",
        "Reno"
      ]
    });
  }

  getBuildingData() {
    this.setState({
      data: [
        "33 Irving Place",
        "22 Irving Place",
        "11 Irving Place",
        "3344 Irving Place",
        "44 Irving Place",
        "77 Irving Place",
        "55 Irving Place",
        "66 Irving Place"
      ]
    });
  }

  componentDidMount() {
    const { type } = this.props;
    type === "world"
      ? this.getWorldData()
      : type === "country"
        ? this.getCountryData()
        : type === "building"
          ? this.getBuildingData()
          : alert("No Data to show");
  }

  handleChange(e) {
    const label = e.target.value;
    const isChecked = e.target.checked;
    this.setState(prevState => ({
      checkedItems: prevState.checkedItems.set(label, isChecked)
    }));
  }
  render() {
    const {
      data,
      checkedItems
    } = this.state;
    return (
      <Content type={data === null ? "progress" : null}>
        {data === null ? (
          <ActivityIndicator size={100} />
        ) : (
          <FlexColumn>
            {data.map(item => (
              <Label
                control={
                  <Checkbox
                    checked={checkedItems.get(item)}
                    value={item}
                    color="default"
                    onChange={this.handleChange}
                  />
                }
                label={item}
                key={item}
              />
            ))}
          </FlexColumn>
        )}
      </Content>
    );
  }
}

export default Contents;

可能你们中的一些人建议我为什么需要在 componentDidUpdate() 中而不是在 handleChange(e) 中调用来自 prop 的函数。 回答:当我设置子组件的状态时,它不会立即更新,所以我无法调用该函数,通过道具传递,更新状态。我需要通过这个函数发送更新的状态。请大家帮帮我!

【问题讨论】:

  • reactjs 中有一种模式。 Lifting State Up。它会解决很多问题。
  • 感谢@Gobinda,值得了解和帮助解决我的问题!
  • 非常欢迎。乐于助人。

标签: reactjs


【解决方案1】:

在你的函数中:

handleChange(e) {
    const label = e.target.value;
    const isChecked = e.target.checked;
    this.setState(prevState => ({
      checkedItems: prevState.checkedItems.set(label, isChecked)
    }));
  }

尝试调用传入的prop函数:

handleChange(e) {
const { getSelectedItem } = this.props;
    const label = e.target.value;
    const isChecked = e.target.checked;
    getSelectedItems(label, isChecked);
  }

您的子组件应该是“无状态”或“PureComponent”,并且本质上应该限制为没有逻辑或状态。您可以使用 props、redux、Mobx 等或新的上下文 API Provider/Consumer 技术传递所有内容。我鼓励你用谷歌搜索其中的一些术语。

* 从上面共享的链接中取出的密钥 *

对于在 React 应用程序中发生变化的任何数据,都应该有一个单一的“事实来源”。通常,状态首先添加到需要它进行渲染的组件中。然后,如果其他组件也需要它,您可以将它提升到它们最近的共同祖先。您应该依赖自上而下的数据流,而不是尝试在不同组件之间同步状态。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 2019-06-23
  • 2019-07-12
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 2019-06-03
相关资源
最近更新 更多