[reactjs] 사용중인 무한 루프

React 16.7-alpha에서 새로운 후크 시스템을 가지고 놀았고 내가 처리중인 상태가 객체 또는 배열 일 때 useEffect에서 무한 루프에 갇혀 있습니다.

먼저 useState를 사용하고 다음과 같은 빈 개체로 시작합니다.

const [obj, setObj] = useState({});

그런 다음 useEffect에서 setObj를 사용하여 다시 빈 개체로 설정합니다. 두 번째 인수로 객체 의 내용 이 변경되지 않은 경우 업데이트되지 않기를 바라면서 [obj]를 전달 합니다. 하지만 계속 업데이트됩니다. 내용에 관계없이 항상 다른 객체이기 때문에 React가 계속 변한다고 생각합니까?

useEffect(() => {
  setIngredients({});
}, [ingredients]);

배열의 경우도 마찬가지지만, 기본적으로 예상대로 루프에 갇히지 않습니다.

이러한 새로운 후크를 사용하여 콘텐츠가 변경되었는지 여부를 확인할 때 객체 및 배열을 어떻게 처리해야합니까?



답변

빈 배열을 useEffect의 두 번째 인수로 전달하면 마운트 및 마운트 해제시에만 실행되므로 무한 루프가 중지됩니다.

useEffect(() => {
  setIngredients({});
}, []);

이것은 https://www.robinwieruch.de/react-hooks/의 React hooks에 대한 블로그 게시물에서 저에게 명확 해졌습니다.


답변

같은 문제가있었습니다. 왜 그들이 문서에서 이것을 언급하지 않았는지 모르겠습니다. Tobias Haugen 답변에 약간을 추가하고 싶습니다.

모든 구성 요소 / 상위 rerender 에서 실행하려면 다음 을 사용해야합니다.

  useEffect(() => {

    // don't know where it can be used :/
  })

컴포넌트 마운트 후 한 번만 실행하려면 (한 번 렌더링 됨) 다음을 사용해야합니다.

  useEffect(() => {

    // do anything only one time if you pass empty array []
    // keep in mind, that component will be rendered one time (with default values) before we get here
  }, [] )

구성 요소 마운트 및 데이터 / 데이터 2 변경 시 한 번만 실행하려면 :

  const [data, setData] = useState(false)
  const [data2, setData2] = useState('default value for first render')
  useEffect(() => {

// if you pass some variable, than component will rerender after component mount one time and second time if this(in my case data or data2) is changed
// if your data is object and you want to trigger this when property of object changed, clone object like this let clone = JSON.parse(JSON.stringify(data)), change it clone.prop = 2 and setData(clone).
// if you do like this 'data.prop=2' without cloning useEffect will not be triggered, because link to data object in momory doesn't changed, even if object changed (as i understand this)
  }, [data, data2] )

대부분의 시간 사용 방법 :

export default function Book({id}) {
  const [book, bookSet] = useState(false)

  useEffect(() => {
    loadBookFromServer()
  }, [id]) // every time id changed, new book will be loaded

  // Remeber that it's not always safe to omit functions from the list of dependencies. Explained in comments.
  async function loadBookFromServer() {
    let response = await fetch('api/book/' + id)
    response  = await response.json()
    bookSet(response)
  }

  if (!book) return false //first render, when useEffect did't triggered yet we will return false

  return <div>{JSON.stringify(book)}</div>
}


답변

같은 문제가 한 번도 발생했고 두 번째 인수에 기본 값을 전달하여 문제를 해결했습니다 [].

객체를 전달하면 React는 객체에 대한 참조 만 저장하고 참조가 변경 될 때 효과를 실행합니다. 이는 일반적으로 매번 매번 발생합니다 (지금은 그렇지 않습니다).

해결책은 객체의 값을 전달하는 것입니다. 당신은 시도 할 수 있습니다,

const obj = { keyA: 'a', keyB: 'b' }

useEffect(() => {
  // do something
}, [Object.values(obj)]);

또는

const obj = { keyA: 'a', keyB: 'b' }

useEffect(() => {
  // do something
}, [obj.keyA, obj.keyB]);


답변

문서 ( https://reactjs.org/docs/hooks-effect.html ) 에서 말했듯 이 useEffect후크는 렌더링 할 때마다 일부 코드가 실행되기를 원할 때 사용 됩니다 . 문서에서 :

useEffect는 모든 렌더링 후에 실행됩니까? 예!

이를 사용자 정의하려면 나중에 동일한 페이지 ( https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects ) 에 표시되는 지침을 따를 수 있습니다 . 기본적으로이 useEffect메서드는 React가 효과를 다시 트리거해야하는지 여부를 결정하기 위해 조사 할 두 번째 인수를 받습니다 .

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

두 번째 인수로 모든 개체를 전달할 수 있습니다. 이 개체가 변경되지 않으면 효과는 첫 번째 탈것 이후에만 발동 됩니다. 개체가 변경되면 효과가 다시 트리거됩니다.


답변

사용자 정의 후크를 구축하는 경우 다음과 같이 기본값으로 무한 루프가 발생할 수 있습니다.

function useMyBadHook(values = {}) {
    useEffect(()=> {
           /* This runs every render, if values is undefined */
        },
        [values]
    )
}

수정 사항은 모든 함수 호출에서 새 객체를 만드는 대신 동일한 객체를 사용하는 것입니다.

const defaultValues = {};
function useMyBadHook(values = defaultValues) {
    useEffect(()=> {
           /* This runs on first call and when values change */
        },
        [values]
    )
}

구성 요소 코드에서이 문제가 발생 하면 ES6 기본값 대신 defaultProps를 사용하면 루프가 수정 될 수 있습니다.

function MyComponent({values}) {
  useEffect(()=> {
       /* do stuff*/
    },[values]
  )
  return null; /* stuff */
}

MyComponent.defaultProps = {
  values = {}
}


답변

이것이 효과가 있는지 확실하지 않지만 다음과 같이 .length를 추가해 볼 수 있습니다.

useEffect(() => {
        // fetch from server and set as obj
}, [obj.length]);

제 경우에는 (배열을 가져 왔습니다!) 마운트시 데이터를 가져 왔고 변경시에만 데이터를 가져 왔고 루프에 들어 가지 않았습니다.


답변

useEffect 끝에 빈 배열을 포함하는 경우 :

useEffect(()=>{
        setText(text);
},[])

한 번 실행됩니다.

배열에 매개 변수도 포함하는 경우 :

useEffect(()=>{
            setText(text);
},[text])

텍스트 매개 변수가 변경 될 때마다 실행됩니다.