Phản ứng các phương thức vòng đời đối với hooks

Dec 16 2020

Tôi có một blog đơn giản với các bài báo. Và tôi muốn viết lại các lớp thành các thành phần và móc chức năng. Bây giờ tôi có logic này trong các phương thức vòng đời cho trang của tôi với biểu mẫu chỉnh sửa / thêm: nó hoạt động tốt.

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

Tôi đã viết lại tất cả thành hook như thế này:

//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. Tôi gặp lỗi - "Quá nhiều kết xuất". tại Codeandbox mã đầy đủ: Codeandbox

Thật lạ, nhưng tại localhost không có lỗi "Quá nhiều kết xuất".

  1. Không biết phải làm gì với phương thức "shouldComponentUpdate" của lớp tôi làm thế nào để viết lại nó thành hook. Đã thử 'ghi nhớ' nhưng không biết làm thế nào để viết trong trường hợp này.

  2. Và dù sao thì tôi vẫn thiếu một cái gì đó, bởi vì tất cả sẽ không hoạt động - nó không cập nhật các trường biểu mẫu đúng cách.

Nếu bạn có kiến ​​thức tốt về react hooks, vui lòng giúp đỡ, hoặc cho một số lời khuyên - làm thế nào để khắc phục nó?

Trả lời

3 Mhmdrz_A Dec 18 2020 at 21:24

Hiệu ứng mà không có sự phụ thuộc đang gây ra "Too many re-renders.": nó chạy sau mỗi lần kết xuất sau đó nó gọi setIsLoadingcập nhật trạng thái ( loading) sẽ khiến thành phần kết xuất lại, thành phần sẽ chạy effectlại và setStatesẽ được gọi lại effect, v.v.

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

để khắc phục sự cố hoặc xóa setIsLoadingkhỏi nó hoặc thêm IsLoadingdưới dạng phụ thuộc.

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

bạn cũng có thể hợp nhất hai hiệu ứng gắn kết thành một như thế này (của bạn cũng đang hoạt động, nhưng tôi nghĩ điều này là phù hợp hơn về mặt phong cách):

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

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

cho gạch đầu dòng thứ hai: về việc viết lại thành phần của bạn shouldComponentUpdate; trước tiên, tôi phải chỉ ra rằng hiện tại của bạn có shouldComponentUpdatevẻ không hợp lý, vì bạn luôn quay lại truevà bạn chỉ sử dụng nó để kích hoạt loadingtrạng thái; và nó không đủ điều kiện để được viết bằng React.memo (tương đương với shouldComponentUpdatetrong các thành phần lớp); vì vậy bạn chỉ cần một cái gì đó được thực thi trên mỗi thay đổi đạo cụ để xác định loadingtrạng thái và bạn có thể sử dụng một hiệu ứng propslàm phụ thuộc của nó như thế này:

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

dựa trên nhận thức của tôi, điều này sẽ thực hiện công việc, (mặc dù rất khó hiểu tại sao bạn viết logic theo cách này ngay từ đầu) và nếu nó không ổn, bạn có thể điều chỉnh logic một chút để phù hợp với nhu cầu của mình, bạn sẽ có ý tưởng về cách hoạt động của hiệu ứng thay đổi đạo cụ. Ngoài ra, nhiều người cần xử lý typeErrortrong trường hợp id, paramshoặc matchkhông tồn tại và Chuỗi tùy chọn có thể là công cụ hữu ích ở đây ->props.match?.params?.id

2 deckele Dec 23 2020 at 23:26

Nếu bạn bỏ lỡ các móc vòng đời cũ, bạn luôn có thể tạo lại chúng dưới dạng móc tùy chỉnh:

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

Cách sử dụng (ví dụ của bạn được cấu trúc lại):

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