programing

다른 구성 요소 경고를 렌더링하는 동안 구성 요소를 업데이트할 수 없습니다.

instargram 2023. 3. 18. 08:16
반응형

다른 구성 요소 경고를 렌더링하는 동안 구성 요소를 업데이트할 수 없습니다.

이 경고는 다음과 같습니다.

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

스택 트레이스에 표시된 위치로 이동하여 모든 설정 상태를 제거했지만 경고는 여전히 지속됩니다.redux 디스패치에 의해 이 문제가 발생할 가능성이 있습니까?

내 코드:

등록. 삭제.

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

저는 이 문제를 안고 있었습니다만, 제가 무엇을 잘못했는지 깨닫기까지는 조금 더 조사했습니다.기능 컴포넌트를 어떻게 쓰는지에 주의를 기울이지 않았습니다.

나는 이것을 하고 있었다:

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

useEffect을 전에getDateMatches()

그래서 다음과 같이 해야 합니다.

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

오류 메시지를 자세히 읽어주세요.내 것은 setState가 불량한 SignIn 컴포넌트를 가리키고 있었습니다.조사해보니 애로우 기능이 아닌 온프레스가 있더군요

이런 식이었다.

onPress={navigation.navigate("Home", { screen: "HomeScreen" })}

다음과 같이 변경했습니다.

onPress={() => navigation.navigate("Home", { screen: "HomeScreen" }) }

오류 메시지는 다음과 같습니다.

할 수 (컴포넌트 갱신할 수 없습니다).ForwardRef(BaseNavigationContainer)컴포넌트 중 (</FONT CHANGE/FONTHANGE:></FONT CHANGE:>)SignIn한 것을 setState() 콜의 를 알 수 있습니다.SignInhttps://reactjs.org/link/setstate-in-render의 SignIn(SignInScreen.tsx:20)에 기재되어 있는 스택트레이스를 따릅니다

레지스터 컴포넌트 렌더 방식에서 컴포넌트 언마운트 방식으로 디스패치를 삭제하여 이 문제를 해결했습니다.이는 로그인 페이지로 리다이렉트하기 직전에 이 로직을 발생시키고 싶었기 때문입니다.일반적으로는 모든 논리를 렌더링 방식 이외의 방법으로 사용하는 것이 가장 좋기 때문에 이전에는 코드가 제대로 작성되지 않았습니다.이것이 장래에 다른 사람에게 도움이 되기를 바랍니다.

리팩터링된 레지스터 구성 요소:

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

고객님의 경우 사용할 수 없는 경우 또는 Redux 때문에 오류가 아닌 경우

하였습니다.setTimeoutuseState이치노

는 부모 한 한 .useState가변성이 있습니다.은 포장하는 입니다.useState( 「」를 사용)setTimeout:

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 (

나는 이것이 중요하다고 생각한다.이 글에서 @Red-Baron은 다음과 같이 지적했다.

@machineghost : 메시지 내용을 잘못 알고 계신 것 같습니다.

부모 상태를 업데이트하는 자녀에게 콜백을 전달하는 것은 문제가 되지 않습니다.그건 항상 괜찮았어요.

이 문제는 첫 번째 컴포넌트가 렌더링 중 한 컴포넌트가 다른 컴포넌트에 업데이트를 큐잉하는 경우입니다.

즉, 이 조작을 실시하지 말아 주세요.

function SomeChildComponent(props) {
    props.updateSomething();
    return <div />
}

하지만 이건 괜찮아

function SomeChildComponent(props) {
    // or make a callback click handler and call it in there
    return <button onClick={props.updateSomething}>Click Me</button>
}

또한 Dan이 여러 번 지적했듯이 렌더링 중에 업데이트를 같은 컴포넌트로 큐잉하는 것도 좋습니다.

function SomeChildComponent(props) {
  const [number, setNumber] = useState(0);

  if(props.someValue > 10 && number < 5) {
    // queue an update while rendering, equivalent to getDerivedStateFromProps
    setNumber(42);
  }

  return <div>{number}</div>
}

리액트 및 머티리얼 UI(MUI)를 사용하여 코드를 다음과 같이 변경했습니다.

<IconButton onClick={setOpenDeleteDialog(false)}>
        <Close />
      </IconButton>

수신인:

<IconButton onClick={() => setOpenDeleteDialog(false)}>
        <Close />
      </IconButton>

간단한 수정

리액트 네비게이션을 사용하고 있으며setParams ★★★★★★★★★★★★★★★★★」setOptions인 방법을 꼭 해요.componentDidMount() 컴포넌트의useEffects()츠미야

최소한의 재현 예

문제의 원인이 무엇인지에 대해 조금 혼란스러웠습니다.즉각 실행할 수 있는 최소한의 예제를 통해 문제를 보다 잘 파악할 수 있었습니다.

index.displaces를 표시합니다.

<!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)

한 이 될 이다.props.setN(1)는 발신기지로부터 발신되고 있습니다만, Babel JSX 변환에 의해 회선 번호가 약간 잘못되어 있습니다.

다른 많은 답변과 마찬가지로 해결책은 다음과 같습니다.

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

직관적으로 이 오류가 발생하는 이유에 대한 일반적인 개념은 다음과 같습니다.

렌더 메서드에서 상태를 업데이트해서는 안 됩니다. 그렇지 않으면 React가 렌더링하는 방법의 순서에 따라 내부 결과가 달라질 수 있습니다.

기능 컴포넌트를 사용할 때는 후크를 사용하는 것이 좋습니다. 경우, ★★★★★★★★★★★★★★★★★★★★★★★★★★★」useEffect렌더링 후에 실행되기 때문에 거기서도 괜찮습니다.

클래스를 사용하면 이 점이 조금 더 명확해지고 다음과 같은 질문을 받았습니다.

그러나 기능 컴포넌트를 사용할 경우 컴포넌트 함수는 렌더링과 콜백을 설정하는 코드이기 때문에 개념적으로 다소 복잡합니다.

저도 같은 문제에 직면해 있었습니다만, 이 수정은 효과가 있었습니다.

setParams/setOptions

useEffect 이외에서는 이 문제가 발생하고 있습니다.따라서 이러한 작업을 useEffect 내에서 수행해 보십시오.아주 잘 될 거야

TL;DR; 제 경우 경고를 수정하기 위해 한 일은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:그 위에 있는 선까지).그래도 상관없습니다만, 에서 로 변경해 보세요!!

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;

예를 들면요.

이 오류가 있는 코드:

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

저도 같은 문제가 있었어요.다음과 같은 함수를 저장하는 상태를 설정했습니다.

// my state definition
const [onConfirm, setOnConfirm] = useState<() => void>();

// then I used this piece of code to update the state
function show(onConfirm: () => void) {
    setOnConfirm(onConfirm);
}

문제는 set On Confirm에서 발생했습니다.React에서 setState는 새로운 값 또는 새로운 값을 반환하는 함수를 사용할 수 있습니다.이 경우 React는 잘못된 onConfirm을 호출하여 새로운 상태를 얻으려고 했습니다.

문제를 해결했습니다.

setOnConfirm(() => onConfirm);

내 케이스는 사용하고 있었다.setState콜백, 대신setState+useEffect

불량 ❌

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

양호 ✅

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

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

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 quantityChangeHandler = (direction) => {
    dispatch(cartActions.changeItemQuantity({title, quantityChange: direction}));
  }
...
          <button onClick={() => quantityChangeHandler(-1)}>-</button>
          <button onClick={() => quantityChangeHandler(1)}>+</button>

처음에는 뚱뚱한 화살표 포장지 없이 직접 전화를 걸었습니다.

위의 몇 가지 답변을 사용하여 다음과 같이 오류를 제거했습니다.

부터

if (value === "newest") {
  dispatch(sortArticlesNewest());
} else {
  dispatch(sortArticlesOldest());
}

이 코드는 내 컴포넌트 최상위 레벨에 있었다.

로.

    const SelectSorting = () => {
  const dispatch = useAppDispatch();

  const {value, onChange} = useSelect();

  useEffect(() => {
    if (value === "newest") {
      dispatch(sortArticlesNewest());
    } else {
      dispatch(sortArticlesOldest());
    }
  }, [dispatch, value]);

언급URL : https://stackoverflow.com/questions/62336340/cannot-update-a-component-while-rendering-a-different-component-warning

반응형