[英]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 無法告訴該鈎子要綁定到哪個組件。 創建一個鈎子會隱式地將它連接到當前渲染上下文,這就是為什么必須在渲染調用中創建鈎子的原因:鈎子構建器在渲染調用之外沒有任何意義,因為他們無法知道它們連接到哪個組件——但是一旦綁定到一個組件,那么它們就需要在任何地方都可用。 實際上,像onClick
或fetch
回調這樣的操作不會發生在渲染上下文中,盡管回調通常是在該上下文中創建的 - 操作回調發生在 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]。 我會試一下:-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.