후크에 대한 라이프 사이클 메소드 반응

Dec 16 2020

기사가있는 간단한 블로그가 있습니다. 그리고 클래스를 기능적 구성 요소와 후크로 다시 작성하고 싶습니다. 이제 편집 / 추가 양식이있는 페이지의 수명주기 메서드에이 논리가 있습니다. 제대로 작동합니다.

componentDidUpdate(prevProps, prevState) {
 if (this.props.match.params.id !== prevProps.match.params.id) {
    if (prevProps.match.params.id) {
        this.props.onUnload();
      }

      this.id = this.props.match.params.id;
        if (this.id) {
            return this.props.onLoad(userService.articles.get(this.id));
        }
        this.props.onLoad(null);
   }
   this.isLoading = false;
}

componentDidMount() {
  if (this.id) {
    this.isLoading = true;
    return this.props.onLoad(userService.articles.get(this.id));
  }
  this.isLoading = false;
  this.props.onLoad(null);
}
   
componentWillUnmount() {
   this.props.onUnload();
}
   
shouldComponentUpdate(newProps, newState) {
   if (this.props.match.params.id !== newProps.match.params.id) {
      this.isLoading = true;
   }
   return true;
}

나는 모든 것을 다음과 같은 후크로 다시 썼습니다.

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);
  }, []);

  useEffect(()=> {
      prevId.current = id;
      }, [id]
  );

  //componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  });

  //componentWillUnmount
  useEffect(() => {
     return props.onUnload();
  }, []);

  1. "다시 렌더링이 너무 많습니다."라는 오류가 발생했습니다. codesandbox에서 전체 코드 : codesandbox

이상하지만 localhost에는 "너무 많은 다시 렌더링"오류가 없습니다.

  1. 내 클래스 "shouldComponentUpdate"메소드로 무엇을 해야할지 모르겠다. 어떻게 그것을 후크에 다시 쓰는지. '메모'를 시도했지만이 경우 어떻게 쓰는지 모르겠습니다.

  2. 그리고 어쨌든 나는 모든 것이 작동하지 않기 때문에 무언가를 놓치고 있습니다-양식 필드를 제대로 업데이트하지 않습니다.

리 액트 훅에 대한 좋은 지식이 있다면 도움을 주거나 조언을 해주시기 바랍니다. 어떻게 고칠 수 있을까요?

답변

3 Mhmdrz_A Dec 18 2020 at 21:24

종속성이없는 효과가 발생합니다 "Too many re-renders.". 렌더링 할 때마다 실행 된 다음 setIsLoadingstate ( loading) 를 업데이트 하여 구성 요소가 다시 렌더링되도록하여 effect다시 실행되고는 다시 setState호출됩니다 effect.

//componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  })

문제를 해결하려면를 제거 setIsLoading하거나 IsLoading종속성으로 추가하십시오 .

//componentDidUpdate
  useEffect(() => {
    ...
    setIsloading(false);
  },[isLoading]);

두 개의 마운트 효과를 다음과 같이 하나로 병합 할 수도 있습니다 (귀하의 효과도 작동하지만 스타일이 더 바람직하다고 생각합니다).

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);

    //componentWillUnmount
    return function () {
      props.onUnload()
    }
  }, []);

두 번째 글 머리 기호 : 컴포넌트의 재 작성에 관하여 shouldComponentUpdate; 먼저 나는 shouldComponentUpdate당신이 항상 반환 true하고 loading상태 를 트리거하는 데에만 사용하고 있기 때문에 기존의 것이 합리적이지 않다는 것을 지적해야합니다 . 그리고 React.memo 로 작성할 자격이 없습니다 (~ shouldComponentUpdate클래스 구성 요소 와 동등 함 ). 따라서 loading상태 를 결정하기 위해 모든 소품 변경에 대해 실행해야하는 작업 만 있으면 되며이를 위해 다음과 props같은 종속성으로 효과를 사용할 수 있습니다 .

//manually keeping old props id
const prevPropsId = useRef();

useEffect(() => {
   if (prevPropsId.current !== props.match.params.id) {
      setIsLoading(true);
   }
   prevPropsId.current = props.match.params.id;
 }, [props.match.params.id]);
// this hook only run if `props.match.params.id` change 

내 깨달음에 따라 이것이 일을 할 것입니다. (처음에 왜 이런 식으로 논리를 작성하는지 이해하는 데 어려움이 있음에도 불구하고) 제대로 작동하지 않으면 필요에 맞게 논리를 약간 조정할 수 있습니다. 소품 변경 효과가 어떻게 작동하는지 알 수 있습니다. 또한 많은 처리해야 typeError경우 id, params또는 match존재하지 않고 옵션 체인은 여기에 편리한 도구가 될 수 있습니다 ->props.match?.params?.id

2 deckele Dec 23 2020 at 23:26

이전 수명주기 후크를 놓친 경우 언제든지 사용자 지정 후크로 다시 만들 수 있습니다.

function useComponentDidMount(effect) {
  useEffect(() => {
    effect();
  }, []);
}
function useComponentWillUnmount(effect) {
  useEffect(() => {
    return effect;
  }, []);
}
function useComponentDidUpdate(effect, dependencies) {
  const hasMountedRef = useRef(false);
  const prevDependenciesRef = useRef(null)
  useEffect(() => {
    // don't run on first render, only on subsequent changes
    if (!hasMountedRef.current) {
      hasMountedRef.current = true;
      prevDependenciesRef.current = dependencies;
      return;
    }
    // run effect with previous dependencies, like in componentDidUpdate!
    effect(...prevDependenciesRef.current || []);
    prevDependenciesRef.current = dependencies;
  }, dependencies)
}

사용법 (리팩터링 된 예제) :

  //componentDidMount
  useComponentDidMount(() => {
    if (id) {
      setIsloading(true);
      props.onLoad(userService.articles.get(id));
      return;
    }
    setIsloading(false);
    props.onLoad(null);
  });

  //componentDidUpdate
  useComponentDidUpdate((prevId) => {
    if (prevId) {
      props.onUnload();
    }
    if (id) {
      props.onLoad(userService.articles.get(id));
      return;
    }
    props.onLoad(null);
    setIsloading(false);
  }, [id]);

  //componentWillUnmount
  useComponentWillUnmount(() => {
     props.onUnload();
  });