我得到这个警告在反应:

index.js:1 Warning: Cannot update a component (`ConnectFunction`) 
while rendering a different component (`Register`). To locate the 
bad setState() call inside `Register` 

我去了堆栈跟踪中指出的位置,并删除了所有设置状态,但警告仍然存在。这可能发生在redux调度?

我的代码:

register.js

class Register extends Component {
  render() {
    if( this.props.registerStatus === SUCCESS) { 
      // Reset register status to allow return to register page
      this.props.dispatch( resetRegisterStatus())  # THIS IS THE LINE THAT CAUSES THE ERROR ACCORDING TO THE STACK TRACE
      return <Redirect push to = {HOME}/>
    }
    return (
      <div style = {{paddingTop: "180px", background: 'radial-gradient(circle, rgba(106,103,103,1) 0%, rgba(36,36,36,1) 100%)', height: "100vh"}}>
        <RegistrationForm/>
      </div>
    );
  }
}

function mapStateToProps( state ) {
  return {
    registerStatus: state.userReducer.registerStatus
  }
}

export default connect ( mapStateToProps ) ( Register );

函数,该函数触发了由register.js调用的registerForm组件中的警告

handleSubmit = async () => {
    if( this.isValidForm() ) { 
      const details = {
        "username": this.state.username,
        "password": this.state.password,
        "email": this.state.email,
        "clearance": this.state.clearance
      }
      await this.props.dispatch( register(details) )
      if( this.props.registerStatus !== SUCCESS && this.mounted ) {
        this.setState( {errorMsg: this.props.registerError})
        this.handleShowError()
      }
    }
    else {
      if( this.mounted ) {
        this.setState( {errorMsg: "Error - registration credentials are invalid!"} )
        this.handleShowError()
      }
    }
  }

堆栈跟踪:


当前回答

此警告从React V16.3.0开始引入。

如果您正在使用功能组件,您可以将setState调用包装到useEffect中。

不能工作的代码:

const HomePage = (props) => {
    
  props.setAuthenticated(true);

  const handleChange = (e) => {
    props.setSearchTerm(e.target.value.toLowerCase());
  };

  return (
    <div key={props.restInfo.storeId} className="container-fluid">
      <ProductList searchResults={props.searchResults} />
    </div>
  );
};

现在你可以把它改成:

const HomePage = (props) => {
  // trigger on component mount
  useEffect(() => {
    props.setAuthenticated(true);
  }, []);

  const handleChange = (e) => {
    props.setSearchTerm(e.target.value.toLowerCase());
  };

  return (
    <div key={props.restInfo.storeId} className="container-fluid">
      <ProductList searchResults={props.searchResults} />
    </div>
  );
};

其他回答

TL,博士; 对于我的案例,我修复警告的方法是将useState更改为useRef

react_devtools_backend.js:2574 Warning: Cannot update a component (`Index`) while rendering a different component (`Router.Consumer`). To locate the bad setState() call inside `Router.Consumer`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
    at Route (http://localhost:3000/main.bundle.js:126692:29)
    at Index (http://localhost:3000/main.bundle.js:144246:25)
    at Switch (http://localhost:3000/main.bundle.js:126894:29)
    at Suspense
    at App
    at AuthProvider (http://localhost:3000/main.bundle.js:144525:23)
    at ErrorBoundary (http://localhost:3000/main.bundle.js:21030:87)
    at Router (http://localhost:3000/main.bundle.js:126327:30)
    at BrowserRouter (http://localhost:3000/main.bundle.js:125948:35)
    at QueryClientProvider (http://localhost:3000/main.bundle.js:124450:21)

我所做的上下文的完整代码(从带有// OLD:的行更改为它们上面的行)。然而,这并不重要,只需尝试从useState更改为useRef!!

import { HOME_PATH, LOGIN_PATH } from '@/constants';
import { NotFoundComponent } from '@/routes';
import React from 'react';
import { Redirect, Route, RouteProps } from 'react-router-dom';
import { useAccess } from '@/access';
import { useAuthContext } from '@/contexts/AuthContext';
import { AccessLevel } from '@/models';

type Props = RouteProps & {
  component: Exclude<RouteProps['component'], undefined>;
  requireAccess: AccessLevel | undefined;
};

export const Index: React.FC<Props> = (props) => {
  const { component: Component, requireAccess, ...rest } = props;

  const { isLoading, isAuth } = useAuthContext();
  const access = useAccess();
  const mounted = React.useRef(false);
  // OLD: const [mounted, setMounted] = React.useState(false);

  return (
    <Route
      {...rest}
      render={(props) => {
        // If in indentifying authentication state as the page initially loads, render a blank page
        if (!mounted.current && isLoading) return null;
        // OLD: if (!mounted && isLoading) return null;

        // 1. Check Authentication is one step
        if (!isAuth && window.location.pathname !== LOGIN_PATH)
          return <Redirect to={LOGIN_PATH} />;
        if (isAuth && window.location.pathname === LOGIN_PATH)
          return <Redirect to={HOME_PATH} />;

        // 2. Authorization is another
        if (requireAccess && !access[requireAccess])
          return <NotFoundComponent />;

        mounted.current = true;
        // OLD: setMounted(true);
        return <Component {...props} />;
      }}
    />
  );
};

export default Index;

我的案例是使用setState回调,而不是setState + useEffect

坏❌

  const closePopover = useCallback(
    () =>
      setOpen((prevOpen) => {
        prevOpen && onOpenChange(false);
        return false;
      }),
    [onOpenChange]
  );

好✅

  const closePopover = useCallback(() => setOpen(false), []);

  useEffect(() => onOpenChange(isOpen), [isOpen, onOpenChange]);

最小复制示例

我有点困惑到底是什么引发了这个问题,有一个最小的立即可运行的例子帮助我更好地理解它:

index . html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.14.7/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function NotMain(props) {
  props.setN(1)
  return <div>NotMain</div>
}

function Main(props) {
  const [n, setN] = React.useState(0)
  return <>
    <NotMain setN={setN} />
    <div>Main {n}</div>
  </>
}

ReactDOM.render(
  <Main/>,
  document.getElementById('root')
);
</script>
</body>
</html>

错误失败:

react-dom.development.js:61警告:在呈现不同的组件(' NotMain ')时不能更新一个组件(' Main ')。要定位' NotMain '内部错误的setState()调用,请按照https://reactjs.org/link/setstate-in-render中描述的堆栈跟踪

后面跟着一个堆栈跟踪:

    at NotMain (<anonymous>:16:9)
    at Main (<anonymous>:21:31)

假定16:9是props.setN(1)被调用的确切行,但是由于Babel JSX翻译,行号有点混乱。

解决方案和其他答案一样,是这样做的:

function NotMain(props) {
  React.useEffect(() => { props.setN(1) }, [])
  return <div>NotMain</div>
}

直观地说,我认为这个错误发生的大致原因是:

你不应该从渲染方法中更新状态,否则会导致不同的结果,这取决于React如何渲染事物的内部顺序。

当使用功能组件时,方法是使用钩子。在我们的例子中,useEffect将在渲染完成后运行,所以我们可以从那里开始执行。

当使用类时,这一点变得更加清晰,例如:

在呈现中调用setState是不可避免的 从渲染方法调用React中的setState()

然而,当使用功能组件时,从概念上讲,事情有点复杂,因为组件函数既是呈现,也是设置回调的代码。

我也面临着同样的问题,对我来说解决方法是如果你在做

setParams - setOptions

在useEffect之外,那么这个问题就发生了。所以尝试在useEffect中做这些事情。这就像魅力一样管用

如果useEffect不能在您的情况下使用或如果错误不是由于Redux

我使用setTimeout将两个useState变量中的一个重定向到回调队列。

我有一个父组件和一个子组件,每个组件中都有useState变量。解决方案是使用setTimeout来包装useState变量:

setTimeout(() => SetFilterData(data), 0);

在下面的例子

父组件

import ExpenseFilter from '../ExpensesFilter'
    
function ExpensesView(props) {
    
    const [filterData, SetFilterData] = useState('')
    
    const GetFilterData = (data) => {
       // SetFilterData(data);

       //*****WRAP useState VARIABLE INSIDE setTimeout WITH 0 TIME AS BELOW.*****
       setTimeout(() => SetFilterData(data), 0);
    
    }
    
    const filteredArray = props.expense.filter(expenseFiltered => 
      expenseFiltered.dateSpent.getFullYear().toString() === filterData);
    
    
    return (
    <Window>
      <div>
        <ExpenseFilter FilterYear = {GetFilterData}></ExpenseFilter>

子组件

const ExpensesFilter = (props) => {
    
    const [filterYear, SetFilterYear] = useState('2022')
    
    const FilterYearListener = (event) => {
        event.preventDefault()
        SetFilterYear(event.target.value)
    }
    
    props.FilterYear(filterYear)
    
    return (