리액트 후크: 빈 배열을 인수로 사용해도 useEffect()가 두 번 호출됩니다.
I am new to reactJS and am writing code so that before the data is loaded from DB, it will show loading message, and then after it is loaded, render components with the loaded data. To do this, I am using both useState hook and useEffect hook. Here is the code:
문제는 console.log에서 확인했을 때 useEffect가 두 번 트리거된다는 것입니다.따라서 코드는 동일한 데이터를 2회 쿼리하고 있으므로 이를 피해야 합니다.
제가 작성한 코드는 다음과 같습니다.
import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'
const indexarray=[]; //The array to which the fetched data will be pushed
function Home() {
const [isLoading,setLoad]=useState(true);
useEffect(()=>{
/*
Query logic to query from DB and push to indexarray
*/
setLoad(false); // To indicate that the loading is complete
})
},[]);
if (isLoading===true){
console.log("Loading");
return <div>This is loading...</div>
}
else {
console.log("Loaded!"); //This is actually logged twice.
return (
<div>
<div className="posts_preview_columns">
{indexarray.map(indexarray=>
<Postspreview
username={indexarray.username}
idThumbnail={indexarray.profile_thumbnail}
nickname={indexarray.nickname}
postThumbnail={indexarray.photolink}
/>
)}
</div>
</div>
);
}
}
export default Home;
왜 두 번 호출되는지, 그리고 코드를 올바르게 수정하는 방법을 누가 좀 도와줄 수 있나요?정말 감사합니다!
console.log를 useEffect 안에 넣습니다.
컴포넌트가 다시 렌더링되는 다른 부작용이 있을 수 있지만 useEffect 자체는 한 번만 호출됩니다.다음 코드를 사용하면 확실히 알 수 있습니다.
useEffect(()=>{
/*
Query logic
*/
console.log('i fire once');
},[]);
로그 "i fire once"가 여러 번 트리거되면 문제가 3가지 중 하나임을 의미합니다.
이 구성 요소가 페이지에 두 번 이상 표시됩니다.
이것은 분명합니다.고객님의 컴포넌트는 여러 번 페이지에 표시되며 각 컴포넌트는 마운트되어 useEffect가 실행됩니다.
트리 위에 있는 것이 마운트 해제 및 재마운트 중입니다.
구성 요소를 강제로 마운트 해제했다가 초기 렌더에서 다시 마운트하는 중입니다.이것은 나무 위에서 일어나는 "키" 변화와 같은 것일 수 있습니다.이 useEffect를 사용하여 한 번만 렌더링될 때까지 각 레벨을 올라가야 합니다.그러면 원인이나 재마운트를 찾을 수 있을 것입니다.
React.Strict 모드가 켜져 있습니다.
StrictMode는 코드의 문제를 감지하고 경고(매우 유용할 수 있음)하기 위해 구성 요소를 두 번 렌더링합니다.
이 답변은 @johnhendirx가 지적하고 @rangfu가 작성했습니다. 이것이 당신의 문제라면 링크를 보고 그에게 사랑을 주세요.이 때문에 문제가 발생할 경우 일반적으로 useEffect를 의도대로 사용하지 않는 것을 의미합니다.베타 문서에는 이에 대한 몇 가지 유용한 정보가 있습니다.
<React>를 삭제합니다.strictMode > from index.js 이 코드는 다음과 같습니다.
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
이것.
root.render(
<App />
);
React Strict Mode는 개발 서버에서 컴포넌트를 2회 렌더링합니다.
대부분의 경우 엄밀한 모드를 사용하도록 설정된 개발 환경에서 문제를 확인합니다.이 문제를 확인하려면 <React>를 검색합니다.Strict Mode >태그 부착 및 삭제 또는 실가동용 빌드.더블 렌더 문제는 없어져야 합니다.React 공식 문서
strict 모드는 부작용을 자동으로 검출할 수 없지만, 조금 더 결정적으로 만들어 부작용을 검출하는 데 도움이 됩니다.이것은 의도적으로 다음 함수를 더블 호출함으로써 이루어집니다.
- useState, useMemo 또는 useReducer로 전달된 함수
- [...]
Strict Mode로 인해 My React Component가 두 번 렌더링됩니다.
인덱스를 확인해 주세요.js
<React.StrictMode>
<App />
</React.StrictMode>
<React>를 삭제합니다.Strict Mode > 래퍼로 한 번 기동합니다.
root.render(
<App />
);
>> react root > index.remove ><React.StrictMode>
React의 특징입니다.리액트를 사용하는 동안 JS.Strict Mode(Strict Mode)StrictMode는 하위 노드에 대한 추가 검사 및 경고를 활성화합니다.왜냐하면 악성코드 사용시 앱이 크래시 되지 않기 때문입니다.Strict Mode는 오류를 검출하기 위해 컴포넌트를 2회 검증하는 안전점검이라고 할 수 있습니다.
이 <React>가 표시됩니다.StricyMode > 를 컴포넌트의 루트로 설정합니다.
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
번 렌더링하는 를할 수 .<React.StrictMode>
확인해보겠습니다. 꼭 돼요.StrictMode
불량 코드 관행의 경우 런타임 오류를 감지합니다.
다음 js를 사용하는 경우 reactStrictMode를 "true"에서 false로 변경합니다.
이것을 next.config.discl에 추가합니다.
reactStrictMode: false,
리액트 18의 2회 컴포넌트 장착에 대한 매우 좋은 설명을 찾았습니다.반응에서 두 번 호출된 UseEffect
주의: 실가동에서는 정상적으로 동작합니다.개발 환경에서 엄격한 모드에서는 오류 및 필요한 청소를 처리하기 위해 의도적으로 2회 마운트를 추가합니다.
는 이것을 useFocusEffect
을 하여 리팩터링을 useEffect
기대했던 대로 되지 않아요.
import React, { useEffect, useState } from 'react'
import { useFocusEffect } from '@react-navigation/native'
const app = () = {
const [isloaded, setLoaded] = useState(false)
useFocusEffect(() => {
if (!isloaded) {
console.log('This should called once')
setLoaded(true)
}
return () => {}
}, [])
}
그리고 화면에 두 번 탐색하는 인스턴스가 있습니다.
결과를 왜 스테이트로 하지 않는지는 확실하지 않습니다.다음 예에서는 이펙트를 한 번 호출하기 때문에 이펙트를 다시 렌더링할 수 있는 미게시 코드로 작업을 했을 가능성이 있습니다.
const App = () => {
const [isLoading, setLoad] = React.useState(true)
const [data, setData] = React.useState([])
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
setLoad(false)//causes re render
setData(data)//causes re render
})
},[])
//first log in console, effect happens after render
console.log('rendering:', data.length, isLoading)
return <pre>{JSON.stringify(data, undefined, 2)}</pre>
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
추가 렌더링을 방지하기 위해 데이터와 로드를 하나의 상태로 결합할 수 있습니다.
const useIsMounted = () => {
const isMounted = React.useRef(false);
React.useEffect(() => {
isMounted.current = true;
return () => isMounted.current = false;
}, []);
return isMounted;
};
const App = () => {
const [result, setResult] = React.useState({
loading: true,
data: []
})
const isMounted = useIsMounted();
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
//before setting state in async function you should
// alsways check if the component is still mounted or
// react will spit out warnings
isMounted.current && setResult({ loading: false, data })
})
},[isMounted])
console.log(
'rendering:',
result.data.length,
result.loading
)
return (
<pre>{JSON.stringify(result.data, undefined, 2)}</pre>
)
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
이것은 이상적인 해결책이 아닐 수 있습니다.하지만 나는 회피책을 썼다.
var ranonce = false;
useEffect(() => {
if (!ranonce) {
//Run you code
ranonce = true
}
}, [])
useEffect는 2회 실행되지만 1회만 실행됩니다.
다른 사람들이 이미 지적했듯이 이 문제는 대부분 React 18.0에서 도입된 Strict Mode 기능에 의해 발생합니다.
왜 이런 일이 일어나는지, 그리고 이 문제를 해결하기 위해 무엇을 할 수 있는지 설명하는 블로그 글을 썼습니다.
하지만 코드를 보고 싶다면, 여기 있습니다.
let initialized = false
useEffect(() => {
if (!initialized) {
initialized = true
// My actual effect logic...
...
}
}, [])
또는 재사용할 수 있는 후크로서:
import type { DependencyList, EffectCallback } from "react"
import { useEffect } from "react"
export function useEffectUnsafe(effect: EffectCallback, deps: DependencyList) {
let initialized = false
useEffect(() => {
if (!initialized) {
initialized = true
effect()
}
}, deps)
}
꼭 필요한 경우에만 이 솔루션을 사용해야 한다는 점을 명심하십시오.
나는 다음과 같은 문제가 있었다.
const [onChainNFTs, setOnChainNFTs] = useState([]);
이 useEffect를 두 번 트리거합니다.
useEffect(() => {
console.log('do something as initial state of onChainNFTs changed'); // triggered 2 times
}, [onChainNFTs]);
MAUNTED ONCE와 setOnChainNFTS의 컴포넌트는 한 번 이상 호출되지 않았음을 확인했으므로 문제가 되지 않았습니다.
OnChainNFT의 초기 상태를 로 변환하여 수정했습니다.null
늘체크를 하고 있습니다.
예.
const [onChainNFTs, setOnChainNFTs] = useState(null);
useEffect(() => {
if (onChainNFTs !== null) {
console.log('do something as initial state of onChainNFTs changed'); // triggered 1 time!
}
}, [onChainNFTs]);
여기 고객님의 용도에 맞는 맞춤 후크가 있습니다.당신 경우에 도움이 될 수도 있어요.
import {
useRef,
EffectCallback,
DependencyList,
useEffect
} from 'react';
/**
*
* @param effect
* @param dependencies
* @description Hook to prevent running the useEffect on the first render
*
*/
export default function useNoInitialEffect(
effect: EffectCallback,
dependancies?: DependencyList
) {
//Preserving the true by default as initial render cycle
const initialRender = useRef(true);
useEffect(() => {
let effectReturns: void | (() => void) = () => {};
/**
* Updating the ref to false on the first render, causing
* subsequent render to execute the effect
*
*/
if (initialRender.current) {
initialRender.current = false;
} else {
effectReturns = effect();
}
/**
* Preserving and allowing the Destructor returned by the effect
* to execute on component unmount and perform cleanup if
* required.
*
*/
if (effectReturns && typeof effectReturns === 'function') {
return effectReturns;
}
return undefined;
}, dependancies);
}
걱정할 것 없어요.React를 개발 모드로 실행하고 있는 경우.그것은 때때로 두 번 실행된다.prod 환경에서 테스트하면 useEffect는 한 번만 실행됩니다.걱정 그만 해!!
새로운 React 문서(현재 베타 버전)에는 이 동작을 정확하게 설명하는 섹션이 있습니다.
문서에서:
일반적으로 정리 기능을 구현하는 것이 답입니다.이펙트가 하고 있던 모든 작업을 정리 기능이 중지 또는 취소해야 합니다.경험의 법칙은 사용자가 한 번 실행한 효과(생산 시처럼)와 설정 → 정리 → 설정 시퀀스(개발 시처럼)를 구분할 수 없다는 것입니다.
따라서 이 경고는 useEffect를 재확인하도록 합니다.보통 청소 기능을 구현해야 합니다.
네, 이것에 대해 코멘트를 하기에는 조금 늦을지도 모르지만, 저는 100% 반응하는 다소 유용한 해결책을 찾았습니다.
이 경우 현재 사용자를 로그아웃하는 POST 요구에 사용하는 토큰이 있습니다.
저는 다음과 같은 상태의 환원제를 사용하고 있습니다.
export const INITIAL_STATE = {
token: null
}
export const logoutReducer = (state, action) => {
switch (action.type) {
case ACTION_SET_TOKEN :
state = {
...state,
[action.name] : action.value
};
return state;
default:
throw new Error(`Invalid action: ${action}`);
}
}
export const ACTION_SET_TOKEN = 0x1;
그런 다음 구성 요소에서 다음과 같이 상태를 확인합니다.
import {useEffect, useReducer} from 'react';
import {INITIAL_STATE, ACTION_SET_TOKEN, logoutReducer} from "../reducers/logoutReducer";
const Logout = () => {
const router = useRouter();
const [state, dispatch] = useReducer(logoutReducer, INITIAL_STATE);
useEffect(() => {
if (!state.token) {
let token = 'x' // .... get your token here, i'm using some event to get my token
dispatch(
{
type : ACTION_SET_TOKEN,
name : 'token',
value : token
}
);
} else {
// make your POST request here
}
}
디자인은 매우 훌륭합니다.POST 요청 후 토큰을 스토리지에서 폐기할 수 있습니다.POST가 성공했는지 확인합니다.비동기 형식의 경우 다음 형식을 사용할 수 있습니다.
POST().then(async() => {}).catch(async() => {}).finally(async() => {})
모두 useEffect 내에서 실행 - 100% 작동하며 RECT 개발자가 염두에 둔 내용이라고 생각합니다.이것에 의해, 실제로는 모든 것이 동작하기 전에 정리(스토리지로부터 토큰을 삭제하는 등)가 더 필요하게 되었습니다만, 지금은, 로그아웃 페이지를 이상한 일 없이 이동할 수 있게 되었습니다.
내 의견으로는...
저 같은 경우에는 엄격한 모드입니다.index.tsx 또는 index.jsx에서 엄밀한 모드 구성 요소 제거
NextJS 13을 사용하여 여기에 온 경우 strict 모드를 삭제하려면 next.config.js 파일에 다음 항목을 추가해야 합니다.
const nextConfig = {
reactStrictMode: false
}
module.exports = nextConfig
프로젝트를 만들 때 기본적으로 "Strict 모드"를 사용했기 때문에 명시적으로 설정해야 합니다.
Code Sandbox를 사용하여 제거하여 문제를 방지하였습니다.
언급URL : https://stackoverflow.com/questions/60618844/react-hooks-useeffect-is-called-twice-even-if-an-empty-array-is-used-as-an-ar
'source' 카테고리의 다른 글
사용 불가능한 필드 집합 내에서 버튼 하나를 사용 가능으로 설정하는 방법 (0) | 2023.04.02 |
---|---|
AngularJs에서 ng-repeat을 X회만 반복합니다. (0) | 2023.04.02 |
MongoDB의 트랜잭션 부족 문제를 어떻게 해결할 것인가? (0) | 2023.04.02 |
재스민 유닛 테스트에서 $q. 모든 약속을 어떻게 해결합니까? (0) | 2023.04.02 |
치명적 오류: 의 '루프' 또는 '스위치' 컨텍스트에 '브레이크'가 없습니다. (0) | 2023.04.02 |