phản ứng móc sử dụngEffect () dọn dẹp chỉ cho componentWillUnmount?


83

Hãy để tôi giải thích kết quả của mã này để hỏi vấn đề của tôi một cách dễ dàng.

const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};

Khi thú ForExample componentcưỡi, 'hiệu ứng' sẽ được ghi lại. Điều này có liên quan đến componentDidMount().

Và bất cứ khi nào tôi thay đổi đầu vào tên, cả 'hiệu ứng' và 'dọn dẹp' sẽ được ghi lại. Ngược lại, sẽ không có thông báo nào được ghi lại bất cứ khi nào tôi thay đổi đầu vào tên người dùng vì tôi đã thêm vào [username]tham số thứ hai của useEffect(). Điều này liên quan đếncomponentDidUpdate()

Cuối cùng, khi các ForExample componentlần ngắt kết nối, 'đã dọn dẹp' sẽ được ghi lại. Điều này có liên quan đến componentWillUnmount().

Tất cả chúng ta đều biết điều đó.

Tóm lại, 'đã dọn dẹp' được gọi bất cứ khi nào thành phần được hiển thị lại (bao gồm cả việc ngắt kết nối)

Nếu tôi muốn làm cho thành phần này chỉ ghi nhật ký 'đã dọn dẹp' tại thời điểm nó được ngắt kết nối, tôi chỉ cần thay đổi tham số thứ hai của useEffect()thành [].

Nhưng Nếu tôi thay đổi [username]thành [], ForExample componentkhông còn triển khai componentDidUpdate()đầu vào tên nữa.

Điều tôi muốn làm là làm cho thành phần chỉ hỗ trợ componentDidUpdate()cho việc nhập tên và componentWillUnmount(). (chỉ ghi nhật ký 'đã dọn dẹp' tại thời điểm thành phần đang được ngắt kết nối)


4
Bạn có thể có 2 hiệu ứng riêng biệt. Một cái được cung cấp một mảng với usernamenó là đối số thứ hai và một cái được cung cấp một mảng trống làm đối số thứ hai.
Tholle

@Tholle Ý của bạn là tôi phải tạo 2 phương thức useEffect () riêng biệt?
koo

1
Vâng, đó là một cách để thực hiện nó.
Tholle

1
@Tholle Tôi nghĩ nó sẽ bị ghi đè bởi phương thức useEffect () cuối cùng. Tôi sẽ thử. Cảm ơn
koo

2
Tuyệt quá! Không có gì. Nó phụ thuộc vào những gì việc dọn dẹp nên làm. 2 hiệu ứng riêng biệt là một giải pháp không tồi.
Tholle

Câu trả lời:


80

Vì quá trình dọn dẹp không phụ thuộc vào username, bạn có thể đặt quá trình dọn dẹp trong một vùng riêng biệt useEffectđược cung cấp một mảng trống làm đối số thứ hai.

Thí dụ

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

  useEffect(
    () => {
      console.log("effect");
    },
    [username]
  );

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>


gương đẹp và sạch. Tôi đang tự hỏi mặc dù. Tôi có thể kích hoạt hiệu ứng sử dụng bằng cách nào đó trên điều hướng đã thay đổi hay tôi sẽ phải di chuyển nó lên trong cây thành phần? Bởi vì khi chỉ dán useEffect "đã dọn dẹp" của bạn, tôi không thấy trình kích hoạt này.
Fabian Bosler

112

bạn có thể sử dụng nhiều hơn một lần sử dụng

ví dụ: nếu biến của tôi là data1, tôi có thể sử dụng tất cả điều này trong thành phần của mình

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );

4
Sự khác biệt giữa useEffects đầu tiên và cuối cùng là gì, useEffect đầu tiên sẽ được gọi trên willmount hoặc didmount , useEffect cuối cùng được trả về hàm gọi lại với mảng trống tại sao? bạn có thể giải thích rõ từng trường hợp sử dụng UseEffect khi nào và làm thế nào chúng ta có thể sử dụng?
siluveru kiran kumar

3
@siluverukirankumar Giá trị trả về (hàm) của một lệnh gọi lại là giá trị đang được gọi khi hủy (sự kiện hủy gắn kết). Đó là lý do tại sao ví dụ cuối cùng là hàm HOC, trả về ngay lập tức. Tham số thứ hai là nơi React sẽ tìm kiếm các thay đổi để chạy lại hook này. Khi nó là một mảng trống, nó sẽ chạy chỉ một lần.
Georgy

3
Cảm ơn @Georgy đã nhận nó useEffect cuối cùng là trở về gọi lại không thấy rõ
siluveru Kiran Kumar

2
Vì vậy, nếu bạn tạo một hook useEffect và nó trả về một hàm .. thì mã trước khi hàm được trả về sẽ chạy dưới dạng componentDidMount ... và mã trong hàm được trả về được gọi cho componentWillUnmount? Nó hơi khó hiểu, vì vậy hãy chắc chắn rằng tôi hiểu đúng. useEffect (() => {// mã để chạy trên mount ... return () => {// mã để chạy gỡ xuống}}) Có đúng không?
Maiya

Tôi khuyên bạn nên đọc overreacted.io/a-complete-guide-to-useeffect Nghĩ về những cái móc trong vòng đời không thực sự ngọt ngào
moto

2
function LegoComponent() {

  const [lego, setLegos] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchLegos().then( legos=> {
      if (isSubscribed) {
        setLegos(legos)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {legos.map(lego=> <li>{lego}</li>)}
    </ul>
  )
}

Trong đoạn mã trên, hàm fetchLegos trả về một lời hứa. Chúng tôi có thể "hủy bỏ" lời hứa bằng cách có một điều kiện trong phạm vi sử dụngEffect, ngăn ứng dụng thiết lập trạng thái sau khi thành phần đã được ngắt kết nối.

Cảnh báo: Không thể thực hiện cập nhật trạng thái React trên một thành phần chưa được gắn kết. Đây là điều không cần thiết, nhưng nó chỉ ra sự rò rỉ bộ nhớ trong ứng dụng của bạn. Để khắc phục, hãy hủy tất cả các đăng ký và tác vụ không đồng bộ trong chức năng dọn dẹp useEffect.


0

useEffect được tách biệt trong phạm vi riêng của nó và được kết xuất tương ứng. Hình ảnh từ https://reactjs.org/docs/hooks-custom.html

nhập mô tả hình ảnh ở đây


Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo rằng câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa .
Богдан Опир

0

Thay vì tạo quá nhiều hàm và phương thức phức tạp, những gì tôi làm là tôi tạo một trình lắng nghe sự kiện và tự động thực hiện gắn kết và ngắt kết nối cho tôi mà không phải lo lắng về việc thực hiện thủ công. Đây là một ví dụ.

//componentDidMount
useEffect( () => {

    window.addEventListener("load",  pageLoad);

    //component will unmount
    return () => {
       
        window.removeEventListener("load", pageLoad);
    }

 });

Bây giờ phần này đã xong, tôi chỉ cần chạy bất cứ thứ gì tôi muốn từ hàm pageLoad như thế này.

const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.