我得到这个警告在反应:

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()
      }
    }
  }

堆栈跟踪:


当前回答

我通过将分配从寄存器组件渲染方法移除到componentwillunmount方法来修复这个问题。这是因为我希望这个逻辑在重定向到登录页面之前发生。一般来说,最好的做法是把所有的逻辑都放在渲染方法之外,所以我的代码之前写得很糟糕。希望这能在将来对其他人有所帮助:)

我重构的寄存器组件:

class Register extends Component {

  componentWillUnmount() {
    // Reset register status to allow return to register page
    if ( this.props.registerStatus !== "" ) this.props.dispatch( resetRegisterStatus() )
  }

  render() {
    if( this.props.registerStatus === SUCCESS ) { 
      return <Redirect push to = {LOGIN}/>
    }
    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>
    );
  }
}

其他回答

在GitHub中遇到类似的问题后,我能够解决这个问题,这让我看到了这条评论,展示了如何精确定位文件中导致错误的确切行。我不知道堆栈跟踪在那里。希望这能帮助到一些人!

请看下面我的解决方案。我只是将函数转换为使用回调。

旧的代码

function TopMenuItems() {
  const dispatch = useDispatch();

  function mountProjectListToReduxStore(projects) {
    const projectDropdown = projects.map((project) => ({
      id: project.id,
      name: project.name,
      organizationId: project.organizationId,
      createdOn: project.createdOn,
      lastModifiedOn: project.lastModifiedOn,
      isComplete: project.isComplete,
    }));
    projectDropdown.sort((a, b) => a.name.localeCompare(b.name));
    dispatch(loadProjectsList(projectDropdown));
    dispatch(setCurrentOrganizationId(projectDropdown[0].organizationId));
  }
};

新代码

function TopMenuItems() {
  const dispatch = useDispatch();

  const mountProjectListToReduxStore = useCallback((projects) => {
    const projectDropdown = projects.map((project) => ({
      id: project.id,
      name: project.name,
      organizationId: project.organizationId,
      createdOn: project.createdOn,
      lastModifiedOn: project.lastModifiedOn,
      isComplete: project.isComplete,
    }));
    projectDropdown.sort((a, b) => a.name.localeCompare(b.name));
    dispatch(loadProjectsList(projectDropdown));
    dispatch(setCurrentOrganizationId(projectDropdown[0].organizationId));
  }, [dispatch]);
};

我只是遇到了这个问题,在我意识到我做错了什么之前,我花了一些时间去挖掘——我只是没有注意我是如何写我的函数组件的。

我是这样做的:

const LiveMatches = (props: LiveMatchesProps) => {
  const {
    dateMatches,
    draftingConfig,
    sportId,
    getDateMatches,
  } = props;

  if (!dateMatches) {
    const date = new Date();
    getDateMatches({ sportId, date });
  };

  return (<div>{component stuff here..}</div>);
};

在分派getDateMatches()的redux调用之前,我刚刚忘记了使用useEffect。

所以它应该是:

const LiveMatches = (props: LiveMatchesProps) => {
  const {
    dateMatches,
    draftingConfig,
    sportId,
    getDateMatches,
  } = props;

  useEffect(() => {
    if (!dateMatches) {
      const date = new Date();
      getDateMatches({ sportId, date });
    }
  }, [dateMatches, getDateMatches, sportId]);

  return (<div>{component stuff here..}</div>);
};

我的例子。

带有错误的代码:

<Form
    initialValues={{ ...kgFormValues, dataflow: dataflows.length > 0 ? dataflows[0].df_tpl_key : "" }}
    onSubmit={() => {}}
    render={({values, dirtyFields }: any) => {
      
      const kgFormValuesUpdated = {
        proj_key: projectKey,
        name: values.name,
        description: values.description,
        public: values.public,
        dataflow: values.dataflow,
        flavours: flavoursSelected,
        skipOCR: values.skipOCR
      };

      if (!_.isEqual(kgFormValues, kgFormValuesUpdated)) {
        setNewKgFormValues(kgFormValuesUpdated);
      }

工作代码:

 <Form
    initialValues={{ ...kgFormValues, dataflow: dataflows.length > 0 ? dataflows[0].df_tpl_key : "" }}
    onSubmit={() => {}}
    render={({ values, dirtyFields }: any) => {
      useEffect(() => {
        const kgFormValuesUpdated = {
          proj_key: projectKey,
          name: values.name,
          description: values.description,
          public: values.public,
          dataflow: values.dataflow,
          flavours: flavoursSelected,
          skipOCR: values.skipOCR
        };

        if (!_.isEqual(kgFormValues, kgFormValuesUpdated)) {
          setNewKgFormValues(kgFormValuesUpdated);
        }
      }, [values]);

      return (

此警告从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;