Tôi có thể đặt trạng thái bên trong móc useEffect không


115

Giả sử tôi có một số trạng thái phụ thuộc vào một số trạng thái khác (ví dụ: khi A thay đổi, tôi muốn B thay đổi).

Có thích hợp để tạo một hook quan sát A và đặt B bên trong hook useEffect không?

Liệu các hiệu ứng có phân tầng như vậy, khi tôi nhấp vào nút, hiệu ứng đầu tiên sẽ kích hoạt, khiến b thay đổi, khiến hiệu ứng thứ hai kích hoạt, trước khi kết xuất tiếp theo? Có bất kỳ nhược điểm nào về hiệu suất khi cấu trúc mã như thế này không?

let MyComponent = props => {
  let [a, setA] = useState(1)
  let [b, setB] = useState(2)
  useEffect(
    () => {
      if (/*some stuff is true*/) {
        setB(3)
      }
    },
    [a],
  )
  useEffect(
    () => {
      // do some stuff
    },
    [b],
  )

  return (
    <button
      onClick={() => {
        setA(5)
      }}
    >
      click me
    </button>
  )
}

Câu trả lời:


32

Các hiệu ứng luôn được thực thi sau khi giai đoạn kết xuất hoàn thành ngay cả khi bạn đặtState bên trong một hiệu ứng, một hiệu ứng khác sẽ đọc trạng thái cập nhật và chỉ thực hiện hành động trên nó sau giai đoạn kết xuất.

Đã nói rằng có lẽ tốt hơn nếu thực hiện cả hai hành động với cùng một hiệu ứng trừ khi có khả năng bcó thể thay đổi do các lý do khác ngoài changing atrường hợp đó bạn cũng muốn thực hiện cùng một logic


4
Vì vậy, nếu A thay đổi B, thành phần sẽ hiển thị hai lần phải không?
Dan Ruswick

2
Tôi cũng muốn biết câu trả lời cho câu hỏi trên
alaboudi

2
@alaboudi Có, nếu A thay đổi gây ra ảnh hưởng sử dụng để chạy bộ B thì thành phần sẽ hiển thị hai lần
Shubham Khatri

95

Nói chung, sử dụng setStatebên trong useEffectsẽ tạo ra một vòng lặp vô hạn mà rất có thể bạn không muốn gây ra. Có một vài ngoại lệ đối với quy tắc đó mà tôi sẽ đề cập sau.

useEffectđược gọi sau mỗi lần kết xuất và khi setStateđược sử dụng bên trong nó, nó sẽ khiến thành phần được kết xuất lại sẽ gọi useEffect, vân vân và vân vân.

Một trong những trường hợp phổ biến mà việc sử dụng useStatebên trong của useEffectsẽ không gây ra vòng lặp vô hạn là khi bạn chuyển một mảng trống làm đối số thứ hai để useEffectthích useEffect(() => {....}, []), điều đó có nghĩa là hàm hiệu ứng nên được gọi một lần: chỉ sau lần gắn kết / hiển thị đầu tiên. Điều này được sử dụng rộng rãi khi bạn đang tìm nạp dữ liệu trong một thành phần và bạn muốn lưu dữ liệu yêu cầu ở trạng thái của thành phần.


3
Một trường hợp khác để sử dụng setStatebên trong useEffectsetting statengười nghe đăng ký bên trong hoặc sự kiện. Nhưng đừng quên hủy đăng ký reactjs.org/docs/hooks-effect.html#effects-with-cleanup
timqian 29-04-19

31
Điều này không hoàn toàn đúng - useState chỉ kích hoạt nếu giá trị bạn đang cập nhật trạng thái khác với giá trị trước đó, do đó, một vòng lặp vô hạn bị ngăn chặn trừ khi giá trị thay đổi giữa các chu kỳ.
RobM

14
Câu trả lời này là không chính xác và không đến mức câu hỏi: trong các mã trong trường hợp, thành phần chỉ hiển thị hai lần khi nút được nhấn vào, không có vòng lặp vô hạn
Bogdan D

14
Đó là một trường hợp sử dụng rất phổ biến để thiết lập trạng thái bên trong useEffect. Hãy nghĩ về việc tải dữ liệu, API cuộc gọi useEffect, lấy dữ liệu, thiết lập bằng cách sử dụng một phần set của useState.
badbod99

3
Tôi đã cập nhật câu trả lời để giải quyết các vấn đề bạn đề cập, bây giờ có tốt hơn không?
Hossam Mourad

52

Đối với các mục đích trong tương lai, điều này cũng có thể giúp:

Sử dụng setState là được, useEffectbạn chỉ cần chú ý như đã mô tả để không tạo vòng lặp.

Nhưng nó không phải là vấn đề duy nhất có thể xảy ra. Xem bên dưới:

Hãy tưởng tượng rằng bạn có một thành phần Compnhận được propstừ cha mẹ và theo một propsthay đổi mà bạn muốn thiết lập Comptrạng thái của. Vì một số lý do, bạn cần thay đổi cho mỗi chỗ dựa theo một cách khác nhau useEffect:

KHÔNG LÀM ĐIỀU NÀY

useEffect(() => {
  setState({ ...state, a: props.a });
}, [props.a]);

useEffect(() => {
  setState({ ...state, b: props.b });
}, [props.b]);

Nó có thể không bao giờ thay đổi trạng thái của một như bạn có thể thấy trong ví dụ này: https://codesandbox.io/s/confident-lederberg-dtx7w

Lý do tại sao điều này xảy ra trong ví dụ này là vì cả hai useEffects đều chạy trong cùng một chu kỳ phản ứng khi bạn thay đổi cả hai prop.aprop.bvì vậy giá trị của {...state}thời điểm bạn làm setStatelà hoàn toàn giống nhau trong cả hai useEffectvì chúng ở trong cùng một ngữ cảnh. Khi bạn chạy cái thứ hai, setStatenó sẽ thay thế cái đầu tiên setState.

LÀM VIỆC NÀY INSTEAD

Giải pháp cho vấn đề này về cơ bản là gọi setStatenhư thế này:

useEffect(() => {
  setState(state => ({ ...state, a: props.a }));
}, [props.a]);

useEffect(() => {
  setState(state => ({ ...state, b: props.b }));
}, [props.b]);

Kiểm tra giải pháp tại đây: https://codesandbox.io/s/mutable-surf-nynlx

Bây giờ, bạn luôn nhận được giá trị cập nhật và chính xác nhất của trạng thái khi bạn tiến hành setState.

Tôi hi vọng điêu nay se giup được ai đo!


2
giải pháp trên đã giúp tôisetName(name => ({ ...name, a: props.a }));
mufaddal_mw

1
điều này đã giúp tôi cũng như trong phần với chức năng mũi tênsetItems(items => [...items, item])
Stefan Zhelyazkov

Chà, bạn thật tuyệt. Bạn đã lưu a * s của tôi hôm nay.
Pratik Soni

Đã dành từ 7 giờ sáng đến 3 giờ chiều mà không có giải pháp và bây giờ bạn đã cứu tôi.
Dinindu Kanchana

21

useEffectcó thể móc vào một chỗ dựa hoặc trạng thái nhất định. vì vậy, điều bạn cần làm để tránh móc vòng lặp vô hạn là ràng buộc một số biến hoặc trạng thái có hiệu lực

Ví dụ:

useEffect(myeffectCallback, [])

hiệu ứng trên sẽ chỉ kích hoạt khi thành phần đã hiển thị. điều này tương tự như componentDidMountvòng đời

const [something, setSomething] = withState(0)
const [myState, setMyState] = withState(0)
useEffect(() => {
  setSomething(0)
}, myState)

hiệu ứng trên sẽ chỉ kích hoạt trạng thái của tôi đã thay đổi, điều này tương tự như componentDidUpdatengoại trừ không phải mọi trạng thái thay đổi sẽ kích hoạt nó.

Bạn có thể đọc thêm chi tiết thông qua liên kết này


Cảm ơn bạn, câu trả lời này giải quyết mảng phụ thuộc của useEffect theo cách mà câu trả lời khác không giải quyết được. Bao gồm một mảng trống làm đối số thứ hai để useEffect sẽ đảm bảo useEffect thực thi sau khi thành phần đã hiển thị, nhưng việc bao gồm một mảng có trạng thái cụ thể hoặc các trạng thái cụ thể sẽ khiến useEffect thực thi khi các trạng thái trong tham chiếu đã thay đổi.
adriaanbd

10

▶ 1. Tôi có thể đặt trạng thái bên trong móc useEffect không?

Về nguyên tắc, bạn có thể thiết lập trạng thái một cách tự do ở nơi bạn cần - bao gồm cả bên trong useEffectngay cả trong quá trình kết xuất . Chỉ cần đảm bảo tránh các vòng lặp vô hạn bằng cách thiết lập Hook depsđúng cách và / hoặc trạng thái có điều kiện.


▶ 2. Cho phép nói rằng tôi có một số trạng thái phụ thuộc vào một số trạng thái khác. Có thích hợp để tạo một hook quan sát A và đặt B bên trong hook useEffect không?

Bạn chỉ cần mô tả các trường hợp sử dụng cổ điển cho useReducer:

useReducerthường thích hợp hơn useStatekhi bạn có logic trạng thái phức tạp liên quan đến nhiều giá trị con hoặc khi trạng thái tiếp theo phụ thuộc vào trạng thái trước đó. (Tài liệu phản ứng )

Khi đặt một biến trạng thái phụ thuộc vào giá trị hiện tại của một biến trạng thái khác, bạn có thể muốn thử thay thế cả hai bằng useReducer. [...] Khi bạn thấy mình đang viết setSomething(something => ...) , đây là thời điểm tốt để cân nhắc sử dụng bộ giảm tốc. ( Dan Abramov, Blog phản ứng quá mức )


▶ 3. Liệu các hiệu ứng có phân tầng như vậy, khi tôi nhấp vào nút, hiệu ứng đầu tiên sẽ kích hoạt, khiến b thay đổi, khiến hiệu ứng thứ hai kích hoạt, trước khi hiển thị tiếp theo?

useEffectluôn chạy sau khi kết xuất được cam kết và các thay đổi DOM được áp dụng. Hiệu ứng đầu tiên kích hoạt, thay đổi bvà gây ra kết xuất. Sau khi kết xuất này hoàn tất, hiệu ứng thứ hai sẽ chạy do các bthay đổi.


▶ 4. Có bất kỳ nhược điểm nào về hiệu suất khi cấu trúc mã như thế này không?

Đúng. Bằng cách gói thay đổi trạng thái của bmột riêng biệt useEffectcho a, trình duyệt có một giai đoạn bố trí / sơn bổ sung - những hiệu ứng này có tiềm năng rõ ràng cho người sử dụng. Nếu không có cách nào bạn muốn useReducerthử, bạn có thể thay đổi btrạng thái cùng với atrực tiếp:

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.