javascript 反应警告:无法从不同组件的函数体内部更新组件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/60526786/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:43:13  来源:igfitidea点击:

React Warning: Cannot update a component from inside the function body of a different component

javascriptreactjsredux

提问by Smith Dwayne

I am using Redux with Class Components in React. Having the below two states in Redux store.

我在 React 中使用 Redux 和 Class Components。在 Redux 商店中具有以下两种状态。

{ spinner: false, refresh: false }

In Parent Components, I have a dispatch function to change this states.

在父组件中,我有一个调度函数来改变这个状态。

class App extends React.Component {
  reloadHandler = () => {
    console.log("[App] reloadComponent");

    this.props.onShowSpinner();
    this.props.onRefresh();
  };

  render() {
    return <Child reloadApp={this.reloadHandler} />;
  }
}

In Child Component, I am trying to reload the parent component like below.

在子组件中,我试图重新加载父组件,如下所示。

class Child extends React.Component {
  static getDerivedStateFromProps(props, state) {
    if (somecondition) {
      // doing some redux store update
      props.reloadApp();
    }
  }

  render() {
    return <button />;
  }
}

I am getting error as below.

我收到如下错误。

Warning: Cannot update a component from inside the function body of a different component.

警告:无法从不同组件的函数体内部更新组件。

How to remove this warning? What I am doing wrong here?

如何消除此警告?我在这里做错了什么?

回答by Jai

It seems that you have latest build of [email protected]. You can find more details about it here. It is specified that you should not setStateof another component from other component.

您似乎拥有最新版本的[email protected]. 您可以在此处找到有关它的更多详细信息。指定您不应setState从其他组件中分离出另一个组件。

from the docs:

从文档:

It is supported to call setState during render, but only for the same component. If you call setState during a render on a different component, you will now see a warning:
Warning: Cannot update a component from inside the function body of a different component.
This warning will help you find application bugs caused by unintentional state changes.In the rare case that you intentionally want to change the state of another component as a result of rendering, you can wrap the setState call into useEffect.

支持在渲染过程中调用 setState,但仅限于同一组件。如果您在不同组件的渲染期间调用 setState,您现在将看到一条警告:
Warning: Cannot update a component from inside the function body of a different component.
此警告将帮助您查找由无意状态更改引起的应用程序错误。在极少数情况下,您故意希望更改另一个组件的状态作为渲染的结果,您可以将 setState 调用包装到 useEffect 中。



Coming to the actual question.

来到实际问题。

I think there is no need of getDerivedStateFromPropsin the child component body. If you want to trigger the bound event. Then you can call it via the onClickof the Child component as i can see it is a <button/>.

我认为getDerivedStateFromProps在子组件主体中不需要。如果要触发绑定事件。然后你可以通过onClick子组件的调用它,因为我可以看到它是一个<button/>.

class Child extends React.Component {
  constructor(props){
    super(props);
    this.updateState = this.updateState.bind(this);
  }
  updateState() { // call this onClick to trigger the update
    if (somecondition) {
      // doing some redux store update
      this.props.reloadApp();
    }
  }

  render() {
    return <button onClick={this.updateState} />;
  }
}

回答by Dominic

For me I was dispatching to my redux store in a React Hook. I had to dispatch in a useEffectto properly sync with the React render cycle:

对我来说,我是在 React Hook 中分派到我的 redux 商店。我必须在 a 中调度以useEffect与 React 渲染周期正确同步:

export const useOrderbookSubscription = marketId => {
  const { data, error, loading } = useSubscription(ORDERBOOK_SUBSCRIPTION, {
    variables: {
      marketId,
    },
  })

  const formattedData = useMemo(() => {
    // DISPATCHING HERE CAUSED THE WARNING
  }, [data])

  // DISPATCHING HERE CAUSED THE WARNING TOO

  // Note: Dispatching to the store has to be done in a useEffect so that React
  // can sync the update with the render cycle otherwise it causes the message:
  // `Warning: Cannot update a component from inside the function body of a different component.`
  useEffect(() => {
    orderbookStore.dispatch(setOrderbookData(formattedData))
  }, [formattedData])

  return { data: formattedData, error, loading }
}

回答by pglezen

I was running into this problem writing a filter component with a few text boxes that allows the user to limit the items in a list within another component. I was tracking my filtered items in Redux state. This solution is essentially that of @Rajnikant; with some sample code.

我在编写一个带有几个文本框的过滤器组件时遇到了这个问题,它允许用户将列表中的项目限制在另一个组件中。我在 Redux 状态下跟踪我过滤的项目。这个解决方案本质上是@Rajnikant 的解决方案;带有一些示例代码。

I received the warning because of following. Note the props.setFilteredItemsin the render function.

我因为关注而收到警告。注意props.setFilteredItems渲染函数中的 。

import {setFilteredItems} from './myActions';
const myFilters = props => {
  const [nameFilter, setNameFilter] = useState('');
  const [cityFilter, setCityFilter] = useState('');

  const filterName = record => record.name.startsWith(nameFilter);
  const filterCity = record => record.city.startsWith(cityFilter);

  const selectedRecords = props.records.filter(rec => filterName(rec) && filterCity(rec));
  props.setFilteredItems(selectedRecords); //  <-- Danger! Updates Redux during a render!

  return <div>
    <input type="text" value={nameFilter} onChange={e => setNameFilter(e.target.value)} />
    <input type="text" value={cityFilter} onChange={e => setCityFilter(e.target.value)} />
  </div>
};

const mapStateToProps = state => ({
  records: state.stuff.items,
  filteredItems: state.stuff.filteredItems
});
const mapDispatchToProps = { setFilteredItems };
export default connect(mapStateToProps, mapDispatchToProps)(myFilters);

When I ran this code with React 16.12.0, I received the warning listed in the topic of this thread in my browser console. Based on the stack trace, the offending line was my props.setFilteredItemsinvocation within the render function. So I simply enclosed the filter invocations and state change in a useEffectas below.

当我使用 React 运行此代码时16.12.0,我在浏览器控制台中收到了此线程主题中列出的警告。根据堆栈跟踪,违规行是我props.setFilteredItems在渲染函数中的调用。所以我简单地将过滤器调用和状态更改包含在 a 中useEffect,如下所示。

import {setFilteredItems} from './myActions';
const myFilters = props => {
  const [nameFilter, setNameFilter] = useState('');
  const [cityFilter, setCityFilter] = useState('');

  useEffect(() => {
    const filterName = record => record.name.startsWith(nameFilter);
    const filterCity = record => record.city.startsWith(cityFilter);

    const selectedRecords = props.records.filter(rec => filterName(rec) && filterCity(rec));
    props.setFilteredItems(selectedRecords); //  <-- OK now; effect runs outside of render.
  }, [nameFilter, cityFilter]);

  return <div>
    <input type="text" value={nameFilter} onChange={e => setNameFilter(e.target.value)} />
    <input type="text" value={cityFilter} onChange={e => setCityFilter(e.target.value)} />
  </div>
};

const mapStateToProps = state => ({
  records: state.stuff.items,
  filteredItems: state.stuff.filteredItems
});
const mapDispatchToProps = { setFilteredItems };
export default connect(mapStateToProps, mapDispatchToProps)(myFilters);

When I first added the useEffectI blew the top off the stack since every invocation of useEffectcaused state change. I had to add an array of skipping effectsso that the effect only ran when the filter fields themselves changed.

当我第一次添加useEffect时,由于每次调用useEffect引起的状态更改,我都将顶部从堆栈中取出。我必须添加一系列跳过效果,以便仅在过滤器字段本身更改时才运行效果。

回答by w1n5rx

Commented some lines of code, but this issue is solvable :) This warnings occur because you are synchronously calling reloadAppinside other class, defer the call to componentDidMount().

注释了一些代码行,但这个问题是可以解决的:) 出现此警告是因为您reloadApp在其他类中同步调用,将调用推迟到componentDidMount().

import React from "react";

export default class App extends React.Component {
  reloadHandler = () => {
    console.log("[App] reloadComponent");

    // this.props.onShowSpinner();
    // this.props.onRefresh();
  };

  render() {
    return <Child reloadApp={this.reloadHandler} />;
  }
}

class Child extends React.Component {
  static getDerivedStateFromProps(props, state) {
    // if (somecondition) {
    // doing some redux store update
    props.reloadApp();
    // }
  }

  componentDidMount(props) {
    if (props) {
      props.reloadApp();
    }
  }

  render() {
    return <h1>This is a child.</h1>;
  }
}

回答by Rajnikant

If you want to invoke some function passed as props automatically from child component then best place is componentDidMount lifecycle methods in case of class components or useEffect hooks in case of functional components as at this point component is fully created and also mounted.

如果你想调用一些从子组件自动作为 props 传递的函数,那么最好的地方是 componentDidMount 生命周期方法(在类组件的情况下)或 useEffect 钩子(在功能组件的情况下),因为此时组件已完全创建并安装。