Làm cách nào để sử dụng thành phầnWillMount () trong React Hook?


174

Trong các tài liệu chính thức của React, nó đề cập đến -

Nếu bạn đã quen thuộc với các phương thức vòng đời của lớp React, bạn có thể nghĩ đến việc sử dụngEffect Hook như thành phầnDidMount, thành phầnDidUpdate và thành phầnWillUnmount kết hợp.

Câu hỏi của tôi là - làm thế nào chúng ta có thể sử dụng componentWillMount()phương pháp vòng đời trong một cái móc?

Câu trả lời:


332

Bạn không thể sử dụng bất kỳ phương pháp vòng đời tồn tại ( componentDidMount, componentDidUpdate, componentWillUnmountvv) trong một cái móc. Chúng chỉ có thể được sử dụng trong các thành phần lớp. Và với Móc bạn chỉ có thể sử dụng trong các thành phần chức năng. Dòng dưới đây đến từ tài liệu React:

Nếu bạn quen với phương pháp Phản ứng lớp vòng đời, bạn có thể nghĩ useEffectHook như componentDidMount, componentDidUpdatecomponentWillUnmountcộng lại.

đề nghị là, bạn có thể bắt chước các phương thức vòng đời này từ thành phần lớp trong một thành phần chức năng.

Mã bên trong componentDidMountchạy một lần khi thành phần được gắn kết. useEffecthook tương đương với hành vi này là

useEffect(() => {
  // Your code here
}, []);

Lưu ý tham số thứ hai ở đây (mảng trống). Điều này sẽ chỉ chạy một lần.

Nếu không có tham số thứ hai , useEffecthook sẽ được gọi trên mỗi kết xuất của thành phần có thể gây nguy hiểm.

useEffect(() => {
  // Your code here
});

componentWillUnmountđược sử dụng để dọn dẹp (như loại bỏ trình nghe sự kiện, hủy bộ hẹn giờ, v.v.). Giả sử bạn đang thêm một người nghe sự kiện componentDidMountvà loại bỏ nó componentWillUnmountnhư dưới đây.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

Hook tương đương với mã trên sẽ như sau

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])

183
Giải thích hay cho các sự kiện Vòng đời khác, nhưng đây không phải là câu hỏi cụ thể về một giải pháp thay thế cho thành phầnWillMount ().
Shiraz

3
Trong các ví dụ PanResponder tôi đã thấy thành phầnWillMount dường như là bắt buộc, nếu không, bạn sẽ nhận được panHandlers không xác định.
Thanh Dror

2
Bây giờ tôi thực sự hiểu useEffect()chức năng, cảm ơn.
Iharob Al Asimi

67
Tại sao đây là một câu trả lời được chấp nhận ?? Bạn đã không đề cập đến một cái móc tương đương chocomponentWillMount
Mykybo

3
@techexpert Câu hỏi yêu cầu tương đương componentWillMount, không componentWillUnmount. Câu trả lời này thực sự không trả lời câu hỏi nào cả, và chỉ lặp lại những gì OP đã ngụ ý để biết.
JoshuaCWebDeveloper

61

hookComponentDidMount

Trong hầu hết các trường hợp useComponentDidMountlà công cụ để sử dụng. Nó sẽ chỉ chạy một lần, sau khi thành phần được gắn kết (kết xuất ban đầu).

 const useComponentDidMount = func => useEffect(func, []);

useComponentWillMount

Điều quan trọng cần lưu ý là trong các thành phần lớp componentWillMountđược coi là di sản. Nếu bạn cần mã chỉ chạy một lần trước khi thành phần được gắn kết, bạn có thể sử dụng hàm tạo. Thêm về nó ở đây . Vì thành phần chức năng không có chất tương tự của hàm tạo, nên sử dụng hook để chạy mã chỉ một lần trước khi gắn kết thành phần có thể có ý nghĩa trong một số trường hợp nhất định. Bạn có thể đạt được nó với một cái móc tùy chỉnh.

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  willMount.current = false;
};

Tuy nhiên, có một cạm bẫy. Không sử dụng nó để đặt không đồng bộ trạng thái của bạn (ví dụ: theo yêu cầu của máy chủ. Như bạn có thể mong đợi nó sẽ ảnh hưởng đến kết xuất ban đầu mà nó sẽ không). Những trường hợp như vậy nên được xử lý với useComponentDidMount.

Bản giới thiệu

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}

Bản demo đầy đủ


18
Đây là câu trả lời duy nhất trả lời câu hỏi và có ý nghĩa. Cảm ơn bạn!
chumakoff

3
Vấn đề duy nhất là bạn có thêm một kết xuất vì có cập nhật trạng thái liên quan. Bằng cách sử dụng một ref thay vì bạn có được hành vi mong muốn mà không cần kết xuất thêm: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.cản = false;}, []); if (willMount.cản) {func (); }}; `
phối lại

2
Việc thực hiện chức năng này componentWillMountdựa trên useEffectcó hai vấn đề. Đầu tiên là không có vòng đời gắn kết trong các thành phần chức năng, cả hai móc sẽ chạy sau khi thành phần được kết xuất, do đó Runs only once before component mountslà sai lệch. Cái thứ hai là componentWillMountđược gọi trên máy chủ kết xuất và useEffectkhông. Nhiều thư viện vẫn dựa vào UNSAFE_componentWillMountvì hiện tại đây là cách duy nhất để kích hoạt phía máy chủ có hiệu lực phụ.
Paolo Moretti

2
@PaoloMoretti, cảm ơn. Móc thành phầnWillMount này, không tương đương chính xác với vòng đời của thành phầnWillMount trong một thành phần lớp. Tuy nhiên, chức năng được truyền cho nó, sẽ chạy ngay lập tức, chỉ lần đầu tiên nó được gọi. Điều này thực tế có nghĩa là nó sẽ chạy trước khi được hiển thị và thậm chí trước khi nó trả về giá trị lần đầu tiên. Chúng ta có thể đồng ý về điều đó? Tôi đồng ý rằng việc sử dụng tên thành phầnWillMount không lý tưởng, vì tên này mang ý nghĩa nhất định từ phiên bản vòng đời của lớp. Có lẽ tôi nên gọi nó là "useRunPreMount".
Cá chép Ben

1
@PaoloMoretti, tôi không hiểu lắm. Tôi không làm việc với SSR, nhưng cam kết của tôi là trên thành phần SSRWillMount chạy hai lần - một lần trên máy chủ và một lần trên máy khách. Tôi nghĩ điều tương tự cũng đúng với cuộc gọi lại được chuyển sang useComponentDidMount. useComponentDidMount chuyển tiếp trên useEffect để ngừng gọi lại. Cho đến khi cuộc gọi lại của useEffect được thực thi, chức năng của thành phần sẽ chạy hai lần - một lần trên máy chủ và một lần trên máy khách. Không phải vậy sao?
Cá chép Ben

53

Theo Reacjs.org, thành phầnWillMount sẽ không được hỗ trợ trong tương lai. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Không cần sử dụng thành phầnWillMount.

Nếu bạn muốn làm một cái gì đó trước khi thành phần được gắn kết, chỉ cần thực hiện nó trong hàm tạo ().

Nếu bạn muốn thực hiện các yêu cầu mạng, đừng thực hiện nó trong thành phầnWillMount. Đó là bởi vì làm điều này sẽ dẫn đến lỗi bất ngờ.

Yêu cầu mạng có thể được thực hiện trong thành phầnDidMount.

Hy vọng nó giúp.


cập nhật ngày 08/03/2019

Lý do tại sao bạn yêu cầu thành phầnWillMount có thể là vì bạn muốn khởi tạo trạng thái trước khi kết xuất.

Chỉ cần làm điều đó trong useState.

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;

hoặc có thể Bạn muốn chạy một hàm trong thành phầnWillMount, ví dụ, nếu mã gốc của bạn trông như thế này:

componentWillMount(){
  console.log('componentWillMount')
}

với hook, tất cả những gì bạn cần làm là loại bỏ phương thức vòng đời:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}

Tôi chỉ muốn thêm một cái gì đó vào câu trả lời đầu tiên về useEffect.

useEffect(()=>{})

useEffect chạy trên mọi kết xuất, nó là sự kết hợp của thành phầnDidUpdate, thành phầnDidMount và thành phầnWillUnmount.

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

Nếu chúng ta thêm một mảng trống trong useEffect thì nó chỉ chạy khi thành phần được gắn kết. Đó là bởi vì useEffect sẽ so sánh mảng bạn truyền cho nó. Vì vậy, nó không phải là một mảng trống. Nó có thể là mảng không thay đổi. Ví dụ: nó có thể là [1,2,3] hoặc ['1,2']. useEffect vẫn chỉ chạy khi thành phần được gắn kết.

Nó phụ thuộc vào bạn cho dù bạn muốn nó chỉ chạy một lần hay chạy sau mỗi lần kết xuất. Sẽ không nguy hiểm nếu bạn quên thêm một mảng miễn là bạn biết bạn đang làm gì.

Tôi đã tạo một mẫu cho hook. Làm ơn hãy kiểm tra nó.

https://codesandbox.io/s/kw6xj153wr


cập nhật ngày 21/08/2019

Nó đã là một màu trắng kể từ khi tôi viết câu trả lời ở trên. Có một điều mà tôi nghĩ bạn cần chú ý. Khi bạn sử dụng

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

Khi phản ứng so sánh các giá trị bạn truyền vào mảng [], nó sử dụng Object.is () để so sánh. Nếu bạn truyền một đối tượng cho nó, chẳng hạn như

useEffect(()=>{},[{name:'Tom'}])

Điều này giống hệt như:

useEffect(()=>{})

Nó sẽ kết xuất lại mỗi lần vì khi Object.is () so sánh một đối tượng, nó sẽ so sánh tham chiếu của nó chứ không phải giá trị của chính nó. Nó giống như lý do tại sao {} === {} trả về false vì các tham chiếu của chúng khác nhau. Nếu bạn vẫn muốn so sánh bản thân đối tượng không phải là tham chiếu, bạn có thể làm một cái gì đó như thế này:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])

17
và câu hỏi là làm thế nào để thực hiện nó bằng móc
Shubham Khatri

3
nhưng bạn không cần phải thực hiện nó với hook vì nó sẽ không được hỗ trợ. Không cần phải học cách làm điều đó với móc.
MING WU

1
Bây giờ bạn đã đề cập rằng thành phầnDidMount là vòng đời chính xác để sử dụng, bạn có thể đã thêm cách thực hiện điều đó trong câu trả lời của mình và sau đó câu trả lời của bạn sẽ có ý nghĩa hơn câu trả lời được chấp nhận
Shubham Khatri

8
Chắc chắn đây phải là câu trả lời được chấp nhận - nó giải thích rằng ElementWillMount không có sẵn trong mô hình hook. Khởi tạo trong các thành phần chức năng được đơn giản hóa - nó chỉ cần là một phần của chức năng
Shiraz

1
Làm thế nào đây là điều tương tự như thành phầnWillMount? Nếu bạn ném mã vào thành phần chức năng thì nó sẽ chạy mọi kết xuất đơn lẻ, không chỉ khi thành phần sắp được gắn kết.
Mã hóa

13

useLayoutEffectcó thể thực hiện điều này với một bộ quan sát trống ( []) nếu chức năng thực sự giống với componentWillMount- nó sẽ chạy trước khi nội dung đầu tiên đến DOM - mặc dù thực tế có hai bản cập nhật nhưng chúng đồng bộ trước khi vẽ lên màn hình.

ví dụ:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

Lợi ích so useStatevới trình khởi tạo / setter hoặc useEffectmặc dù nó có thể tính toán vượt qua kết xuất, không có kết xuất lại thực tế cho DOM mà người dùng sẽ nhận thấy và nó được chạy trước kết xuất đáng chú ý đầu tiên, không phải là trường hợp cho useEffect. Nhược điểm tất nhiên là một chút chậm trễ trong kết xuất đầu tiên của bạn vì việc kiểm tra / cập nhật phải xảy ra trước khi vẽ lên màn hình. Nó thực sự phụ thuộc vào trường hợp sử dụng của bạn, mặc dù.

Tôi nghĩ cá nhân, useMemovẫn ổn trong một số trường hợp thích hợp khi bạn cần phải làm một việc gì đó nặng nề - miễn là bạn nhớ rằng đó là ngoại lệ so với chuẩn mực.


3
sử dụngLayoutEffect là cách để đi !!!! Câu trả lời này cho câu hỏi của tôi liên quan đến việc kiểm tra xem người dùng đã đăng nhập chưa. (Vấn đề là, các thành phần sẽ tải, sau đó kiểm tra xem người dùng có đăng nhập không.) Câu hỏi của tôi là, đây có phải là thông lệ tiêu chuẩn không? Tôi không thấy ở quá nhiều nơi
Jessica

1
Vâng, nó khá phổ biến; cũng được đề cập trong các tài liệu React chính thức - chỉ trong văn bản nhỏ hơn do sự phân nhánh của kết xuất DOM kép để chạy logic trước khi người dùng thông báo.
rob2d

Trên thực tế, nó chạy sau khi kết xuất thành phần. Vì vậy, nó hoàn toàn khác với thành phầnWillMount.
Jiri Mihal

6

Tôi đã viết một hook tùy chỉnh sẽ chạy một chức năng một lần trước khi kết xuất đầu tiên.

sử dụngB BeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

Sử dụng:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => { 
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
}

6

Đây là cách tôi mô phỏng hàm tạo trong các thành phần chức năng bằng cách sử dụng useRefhook:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}

Dưới đây là ví dụ về vòng đời:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props

Tất nhiên các thành phần Lớp không có Bodycác bước, không thể thực hiện mô phỏng 1: 1 do các khái niệm khác nhau về chức năng và các lớp.


Tôi đã không đi sâu vào ví dụ của bạn nhưng đoạn mã đầu tiên của bạn hoạt động với tôi, Cảm ơn bạn!
SAndriy

3

Có một cách giải quyết tốt đẹp để thực hiện componentDidMountcomponentWillUnmountvới useEffect.

Dựa trên tài liệu này, useEffectcó thể trả về chức năng "dọn dẹp". chức năng này sẽ không được gọi trong useEffectcuộc gọi đầu tiên , chỉ trong các cuộc gọi tiếp theo.

Do đó, nếu chúng ta sử dụng useEffecthook mà không có phụ thuộc nào cả, hook sẽ chỉ được gọi khi thành phần được gắn kết và chức năng "dọn dẹp" được gọi khi không kết nối thành phần.

useEffect(() => {
    console.log('componentDidMount');

    return () => {
        console.log('componentWillUnmount');
    };
}, []);

Cuộc gọi chức năng trả lại dọn dẹp chỉ được gọi khi thành phần không được kết nối.

Hi vọng điêu nay co ich.


2
Làm thế nào để giúp đỡ nếu nó không có gì để làm với thành phầnWillMount ? Tui bỏ lỡ điều gì vậy?
ZenVentzi

Vâng, bạn đang thiếu thực tế là trong cùng một useEffectcuộc gọi, bạn có được chức năng tương tự componentWillMountcomponentWillUnmounttheo một cách tốt đẹp và sạch sẽ
AfikDeri

Điều đó không đúng, useEffectchỉ chạy sau khi kết xuất trong khi componentWillMountchạy trước khi kết xuất thành phần.
Mã hóa

@Overcode Tôi đã nói về componentDidMountkhông componentWillMount. Tôi đã bỏ lỡ điều đó trong câu hỏi, xấu của tôi.
AfikDeri

1

Bạn có thể hack hook useMemo để bắt chước một sự kiện vòng đời thành phầnWillMount. Cứ làm đi:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentWillMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};

Bạn sẽ cần giữ hook useMemo trước mọi thứ tương tác với trạng thái của bạn. Đây không phải là cách nó được dự định nhưng nó hoạt động với tôi cho tất cả các vấn đề của thành phầnWillMount.

Điều này hoạt động vì useMemo không yêu cầu trả về một giá trị và bạn không thực sự phải sử dụng nó như bất cứ thứ gì, nhưng vì nó ghi nhớ một giá trị dựa trên các phụ thuộc sẽ chỉ chạy một lần ("[]") và nó nằm trên thành phần của chúng tôi chạy một lần khi thành phần gắn kết trước bất cứ điều gì khác.


0

https://reactjs.org/docs/hooks-reference.html#usememo

Hãy nhớ rằng hàm được truyền cho useMemo chạy trong khi kết xuất. Đừng làm bất cứ điều gì mà bạn thường không làm trong khi kết xuất. Ví dụ: các tác dụng phụ thuộc về useEffect, không sử dụngMemo.


usememo là để tối ưu hóa hiệu suất. Một hook sẽ được hiển thị lại sau khi đã được gắn nếu thay đổi prop, làm mất mục đích của tác giả.
max54

0

Câu trả lời của Ben Carp dường như chỉ có một giá trị với tôi.

Nhưng vì chúng tôi đang sử dụng các cách chức năng, một cách tiếp cận khác có thể được hưởng lợi từ việc đóng cửa và HoC:

const InjectWillmount = function(Node, willMountCallback) {
  let isCalled = true;
  return function() {
    if (isCalled) {
      willMountCallback();
      isCalled = false;
    }
    return Node;
  };
};

Sau đó sử dụng nó:

const YourNewComponent = InjectWillmount(<YourComponent />, () => {
  console.log("your pre-mount logic here");
});

0

Câu trả lời ngắn cho câu hỏi ban đầu của bạn , làm thế nào componentWillMountcó thể được sử dụng với React Hook:

componentWillMountđang bị phản đối và coi di sản . Đề nghị phản ứng :

Nói chung, chúng tôi khuyên bạn nên sử dụng hàm tạo () thay vì khởi tạo trạng thái.

Bây giờ trong Câu hỏi thường gặp về Hook bạn tìm hiểu, tương đương với hàm tạo của lớp cho các thành phần hàm là gì:

constructor: Các thành phần chức năng không cần một constructor. Bạn có thể khởi tạo trạng thái trong lệnh gọi useState. Nếu tính toán trạng thái ban đầu là đắt tiền, bạn có thể chuyển một hàm để sử dụngState.

Vì vậy, một ví dụ sử dụng componentWillMounttrông như thế này:

const MyComp = () => {
  const [state, setState] = useState(42) // set initial value directly in useState 
  const [state2, setState2] = useState(createInitVal) // call complex computation

  return <div>{state},{state2}</div>
};

const createInitVal = () => { /* ... complex computation or other logic */ return 42; };

0

Chỉ cần thêm một mảng phụ thuộc trống trong useEffect nó sẽ hoạt động như componentDidMount.

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);

0

Có một mẹo đơn giản để mô phỏng componentDidMountcomponentWillUnmountbằng cách sử dụng useEffect:

useEffect(() => {
  console.log("componentDidMount");

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
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.