簡體   English   中英

是否可以在功能組件之外使用 React Hooks,或者我必須使用 mobx 或 redux?

[英]Is it possible to use React Hooks outside of functional component, or i have to use mobx or redux?

我是 React 的新手,當我閱讀文檔時,我發現有兩種方法可以實現 React 組件,基於函數的和基於類的。 我知道在 React 16.8 之前,不可能在功能組件中管理 state,但之后有 React Hooks。

問題是,React Hooks 似乎有一個限制,它們只能在功能組件中使用。 以服務端-客戶端為例,在收到 401 時需要更改isAuthenticated state。

//client.js
import { useUserDispatch, signOut } from "auth";

export function request(url, args) {
  var dispatch = useUserDispatch();
  return fetch(url, args).then(response => {
  if (response.status === 401) {
    logout(dispatch);
  }
}
);

//auth.js
import React from "react";

var UserStateContext = React.createContext();
var UserDispatchContext = React.createContext();

function userReducer(state, action) {
  ...
}

function UserProvider({ children }) {
  var [state, dispatch] = React.useReducer(userReducer, {
    isAuthenticated: false,
  });

  return (
    <UserStateContext.Provider value={state}>
      <UserDispatchContext.Provider value={dispatch}>
        {children}
      </UserDispatchContext.Provider>
    </UserStateContext.Provider>
  );
}

function useUserState() {
  return React.useContext(UserStateContext);
}

function useUserDispatch() {
  return React.useContext(UserDispatchContext);
}

function signOut(dispatch) {
  dispatch({});
}

export { UserProvider, useUserState, useUserDispatch, loginUser, signOut };

上面的客戶端代碼將產生錯誤“只能在 function 組件的主體內部調用 Hooks”。 所以也許我必須將行var dispatch = useUserDispatch()向上移動到調用request的組件,並將dispatch作為 props 傳遞給request 我覺得這是不對的,不僅request被迫關心一些無意義的(對它) dispatch ,而且這個dispatch會傳播到組件需要request的任何地方。

對於基於類的組件, this.state也不能解決這個問題,但至少我可以使用 mobx。

那么還有其他一些理想的方法來解決這個問題嗎?

Mobx 和 hooks 在實現上非常相似。 兩者都使用某種意義上的“全局”渲染上下文。 React 將渲染上下文與組件渲染上下文聯系起來,但 Mobx 將渲染上下文分開。 因此,這意味着必須在組件渲染生命周期內創建掛鈎(但有時可以在該上下文之外調用)。 Mobx-react 將 Mobx 渲染生命周期與反應生命周期聯系起來,當觀察到的對象發生變化時觸發反應重新渲染。 所以 Mobx-react 將 React 渲染上下文嵌套在 Mobx 渲染上下文中。

React 在內部跟蹤 hooks 的次數和在組件渲染周期內調用 hook 的順序。 另一方面,Mobx 使用代理包裝任何“可觀察的” object,讓 Mobx 上下文知道在 Mobx“運行上下文”(本質上是autorun調用)期間是否引用了它的任何屬性。 然后,當一個屬性發生變化時,Mobx 知道什么“運行上下文”關心該屬性,並重新運行這些上下文。 這意味着您可以在任何可以訪問可觀察 object 的地方更改其屬性,Mobx 會對其做出反應。

對於 react state 鈎子,react 為 state ZA8CFDE6331BD59EB2AC96B8.F 提供了一個自定義設置器 function 然后 React 使用對該 setter 的調用來了解何時需要重新渲染組件。 setter可以在任何地方使用,甚至在 React 渲染之外,但您只能在渲染調用中創建該鈎子,因為否則 react 無法告訴該鈎子要綁定到哪個組件。 創建一個鈎子會隱式地將它連接到當前渲染上下文,這就是為什么必須在渲染調用中創建鈎子的原因:鈎子構建器在渲染調用之外沒有任何意義,因為他們無法知道它們連接到哪個組件——但是一旦綁定到一個組件,那么它們就需要在任何地方都可用。 實際上,像onClickfetch回調這樣的操作不會發生在渲染上下文中,盡管回調通常在該上下文中創建的 - 操作回調發生在 react 完成渲染之后(因為 javascript 是單線程的,所以渲染 ZC1C425Z068E68384F1C 必須完成541AB7A94F1C在其他任何事情發生之前)。

Hooks 作為基於 class 的組件的替代品,您應該為您的項目選擇一個並堅持使用它,不要混淆它。 創建鈎子有一些動機,正如在文檔中更好地說明的那樣:鈎子動機

您可以單獨創建鈎子函數,但它們是由組件使用的。 這類似於將 HOC(高階組件)與基於 class 的組件一起使用。

const myHook = () => {
  [foo, setFoo] = useState('john')
  // use effect for example if you need to run something at state updates
  useEffect(() => { 
    // do something on foo changes
  }, [foo])

  return [foo, setFoo] // returning state and setState you can use them by your component
}

現在你有了一個可重用的鈎子,你可以在你的組件上消費:

const myComponent = (props) => {
  [foo, setFoo] = myHook()

  const handleFoo = () => { 
    // some logic
    setFoo(someValue)
  }      
  return (
    <div>
      <span>{foo}<span>
      <button onClick={handleFoo}>click</button>
    </div>
  )
}

obs:您現在應該避免將變量聲明為var ,大多數情況下選擇const ,如果它是需要更新的值變量(如數字),請使用let

是的,你必須使用 Redux 或 MobX 來解決這個問題。 您必須在 Redux 或 MobX 的全局 state 中維護isAuthenticated state。 然后進行一個可以命名為toggleAuthState的操作並將其傳遞給子組件並從那里切換 state。

您也可以在這種情況下使用功能組件。 基於 Class 的組件並非強制使用 MobX 或 Redux。 如果您將 HOC 作為Container維護,則可以將操作和狀態傳遞給子級。

我正在展示一個使用容器作為 HOC 的示例:

// Container
import React from "react"
import * as actions from "../actions" 
import ChildComponent from "../components/ChildComponent"
import { connect } from "react-redux"
import { bindActionCreators } from "redux"

const Container = props => <ChildComponent { ...props } />

const mapStateToProps = state => ({ ...state })

const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(Container)

然后在ChildComponent中,您可以在需要時使用您的狀態和調度操作。

當你創建一個鈎子時,你必須參考鈎子規則

您只能從反應函數中調用鈎子。 不要從常規的 JavaScript 函數調用 Hooks。 相反,您可以:

✅ 從 React function 組件調用 Hooks。 ✅ 從自定義 Hooks 調用 Hooks(在此頁面上了解它們)。

如果你想創建一個可重用的鈎子,那么你可以為你的函數創建一個自定義的鈎子。 你可以在一個鈎子中調用盡可能多的函數。

例如,這里我將request function 重構為鈎子。

export function useRequest(url, args) {
  var userDispatch = useUserDispatch();
  const fetcher = React.useCallback(() => {
    return new Promise((resolve, reject) =>
      fetch(url, args)
        .then((response) => {
          if (response.status === 401) {
            logout();
            reject();
          }

          resolve(response);
        })
        .catch(reject)
    );
  }, [url, args]);

  return [fetcher, userDispatch];
}

然后消耗它。

function App() {
  const [fetch, userDispatch] = useRequest("/url", {});
  React.useEffect(() => {
    fetch().then((response) => {
      userDispatch({ type: "USER_REQUEST", payload: response });
    });
  }, []);
  return <div>Hello world</div>;
}

我也是這個時候來的。 長話短說,如果您想自己手動完成所有工作,您需要使用 Redux 和 Thunk with Async Logic,如以下鏈接 [1] 中的示例詳細說明。

[1] https://redux.js.org/tutorials/essentials/part-5-async-logic

還有另一種解決方案通過異步 API(可以與 OpenAPI 和 GraphQL 一起使用,處理請求,提供具有生命周期的緩存等)提供開箱即用的體驗,包裝來自 [1] 及其稱為RTK 查詢[2] 的內容。

[2] https://redux-toolkit.js.org/rtk-query/overview

下圖直觀地解釋了 [1] 過程.. 但我認為RTK Query [2] 將所有內容包裝在一個地方,可能是更好的解決方案。 有一個快速入門指南 [3]。 我會試一下:-)

[3] https://redux-toolkit.js.org/tutorials/rtk-query/

暫無
暫無

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

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