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

Smi*_*yne 50 javascript reactjs redux

我在 React 中将 Redux 与类组件一起使用。在 Redux 商店中具有以下两种状态。

{ spinner: false, refresh: false }
Run Code Online (Sandbox Code Playgroud)

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

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

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

  render() {
    return <Child reloadApp={this.reloadHandler} />;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  render() {
    return <button />;
  }
}
Run Code Online (Sandbox Code Playgroud)

我收到如下错误。

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

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

Dom*_*nic 46

对我来说,我是在 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 }
}
Run Code Online (Sandbox Code Playgroud)


Jai*_*Jai 21

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

从文档:

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


来到实际问题。

我认为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} />;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @f0rfun 你可以将其发布在这里。这肯定会帮助其他人,因为这是新事物。 (2认同)

rad*_*huq 21

如果您的代码在满足如下条件时调用父组件中的函数:

const ListOfUsersComponent = ({ handleNoUsersLoaded }) => {
    const { data, loading, error } = useQuery(QUERY);

    if (data && data.users.length === 0) {
        return handleNoUsersLoaded();
    }

    return (
        <div>
            <p>Users are loaded.</p>
        </div>
    );
};
Run Code Online (Sandbox Code Playgroud)

尝试将条件包装在 a 中useEffect

const ListOfUsersComponent = ({ handleNoUsersLoaded }) => {
    const { data, loading, error } = useQuery(QUERY);

    useEffect(() => {
        if (data && data.users.length === 0) {
            return handleNoUsersLoaded();
        }
    }, [data, handleNoUsersLoaded]);

    return (
        <div>
            <p>Users are loaded.</p>
        </div>
    );
};
Run Code Online (Sandbox Code Playgroud)


Jas*_*ing 13

相同的错误但不同的场景

tl;博士包装状态更新setTimeout修复它。

这种情况导致了 IMO 是有效用例的问题。

const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setSomeState(someNewValue);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);

Run Code Online (Sandbox Code Playgroud)

使固定

const [someState, setSomeState] = useState(someValue);
const doUpdate = useRef((someNewValue) => {
  setTimeout(() => {
   setSomeState(someNewValue);
  }, 0);
}).current;
return (
  <SomeComponent onSomeUpdate={doUpdate} />
);

Run Code Online (Sandbox Code Playgroud)

  • `在 setTimeout 中包装状态更新可以解决这个问题`但是为什么呢? (3认同)
  • 虽然我不知道为什么它会解决,但你的解决方案对我来说效果很好。谢谢杰森! (2认同)

Ana*_*kzz 8

就我而言,我错过了箭头功能 ()=>{}

代替 onDismiss={()=>{/*do something*/}}

我有它 onDismiss={/*do something*/}


Muh*_*sal 6

升级 react 和 react native 后我遇到了同样的问题,我只是通过将 props.navigation.setOptions 放在 useEffect 中解决了这个问题。如果有人面临与我相同的问题,我只想建议他改变您的状态或在 useEffect 中进行任何操作