簡體   English   中英

在 React 中,如何在所有組件中擁有自定義鈎子的單個實例?

[英]In React, how to have a single instance of a custom hook in all components?

我有幾個組件需要訪問相同的玩家列表。 這個玩家列表不會改變,但我不知道哪個組件首先需要它。 我的想法是,無論誰組件首先需要它(axios 請求),都會更新我的 redux 商店,然后其他組件將為此使用相同的自定義掛鈎。

export default function usePlayersList() {
  const {list, loading, error} = useSelector(state => state.accounts.players)
  const dispatch = useDispatch()
  
  useEffect(() => {
    try {
      const response = authAxios.get(endpoints.players.retrieve)
      response.then(res => {
          dispatch({
            type: "SET_PLAYERS_LIST",
            payload: {
              error: false,
              loading: false,
              list: res.data.payload
            }
          })
        })
    } catch (e) {
      console.log(e)
      dispatch({
        type: "SET_PLAYERS_LIST", 
        payload: {
          error: true,
          loading: false,
          list: []
        }
      })
    }
  }, [dispatch])
  return {list, loading, error}
}

這是我為此定制的鈎子。 想知道這樣做是否有任何不便或更好的模式。 提前致謝。

順便說一句...更好的是,我會在 usePlayersList 中將加載和錯誤設置為 useState 變量(而不是將它們發送到 redux 狀態)。 由於這導致我出錯(假設這個 state 對每個組件來說都是獨立的),我將它們發送到商店。

最好的問候,費利佩。

TL;DR 使用上下文

解釋:

每個組件都獲取其自定義掛鈎的實例。

在下面的示例中,我們可以看到調用setState會更改Bla組件中的 state 而不會更改Foo組件中的值:

 const { Fragment, useState, useEffect } = React; const useCustomHook = () => { const [state, setState] = useState('old'); return [state, setState]; }; const Foo = () => { const [state] = useCustomHook(); return <div>state in Foo component: {state}</div>; }; const Bla = () => { const [state, setState] = useCustomHook(); return ( <div> <div>state in Bla component: {state}</div> <button onClick={() => setState("new")}>setState</button> </div> ); }; function App() { return ( <Fragment> <Foo /> <Bla /> </Fragment> ); } ReactDOM.render(<App />, document.querySelector('#root'));
 <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>

可以使用上下文和自定義掛鈎的組合來解決上述問題:

 const { createContext, useContext, useState, useEffect } = React; const Context = createContext(); const ContextProvider = ({ children }) => { const value = useState("old"); return <Context.Provider value={value} children={children} />; }; const useCustomHook = () => { const [state, setState] = useContext(Context); return [state, setState]; }; const Foo = () => { const [state] = useCustomHook(); return <div>state in Foo component: {state}</div>; }; const Bla = () => { const [state, setState] = useCustomHook(); return ( <div> <div>state in Bla component: {state}</div> <button onClick={() => setState("new")}>setState</button> </div> ); }; function App() { return ( <ContextProvider> <Foo /> <Bla /> </ContextProvider> ); } ReactDOM.render(<App />, document.querySelector('#root'));
 <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>


特定於 OP 需求的原始答案:

定義上下文:

import React from 'react';

const PlayersContext = React.createContext();
const PlayersProvider = PlayersContext.Provider;

export const usePlayersContext = () => React.useContext(PlayersContext);

export default function PlayersProvider({ children }) {
  // add all the logic, side effects here and pass them to value
  const { list, loading, error } = useSelector(
    (state) => state.accounts.players
  );
  const dispatch = useDispatch();

  useEffect(() => {
    try {
      const response = authAxios.get(endpoints.players.retrieve);
      response.then((res) => {
        dispatch({
          type: 'SET_PLAYERS_LIST',
          payload: {
            error: false,
            loading: false,
            list: res.data.payload,
          },
        });
      });
    } catch (e) {
      console.log(e);
      dispatch({
        type: 'SET_PLAYERS_LIST',
        payload: {
          error: true,
          loading: false,
          list: [],
        },
      });
    }
  }, [dispatch]);

  return <PlayersProvider value={{ list, loading, error }}>{children}</PlayersProvider>;
}

PlayersProvider作為父級添加到需要訪問玩家的組件中。

在這些子組件中:

import {usePlayersContext} from 'contextfilepath'

function Child() {
  
  const { list, loading, error } = usePlayersContext()
  return ( ... )
}

您還可以在Provider本身而不是 redux 中維護 state。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM