记录一些自己写过或用到过的自定义hooks,尽量 持续更新
useSelector
实现了一个自定义的 useSelector Hook,它的作用类似于 React-Redux 中的 useSelector。通过该 Hook,你可以从全局状态中选择特定的部分,并订阅状态的变化。这个 Hook 利用了 store.getState() 来获取当前状态,并监听状态的变化,当状态发生变化时更新组件的状态。
import { useState, useEffect, useRef } from 'react'
const store = window.reactStore //获取全局状态管理的store
const defaultEqualityFn = (a, b) => a === b // defaultEqualityFn 用于检查新状态和当前状态是否相等
const useSelector = (selector, equalityFn = defaultEqualityFn) => {
const [state, setState] = useState(() => selector(store.getState()))
const latestSelector = useRef(selector) // useRef 保持值的持续性,而且不会触发组件重新渲染
const latestEqualityFn = useRef(equalityFn)
const latestState = useRef(state)
useEffect(() => {
latestSelector.current = selector
latestEqualityFn.current = equalityFn
})
useEffect(() => {
const checkForUpdate = () => {
const newState = latestSelector.current(store.getState())
if (!latestEqualityFn.current(newState, latestState.current)) {
latestState.current = newState
setState(newState)
}
}
const unsubscribe = store.subscribe(checkForUpdate)
checkForUpdate()
return () => {
unsubscribe()
}
}, [])
return state
}
export default useSelector
解析:
1.定义 useSelector Hook
const useSelector = (selector, equalityFn = defaultEqualityFn)
useSelector接受两个参数- selector:选择器函数,用于从全局状态中选择某一部分状态
- equalityFn:用于比较新状态和当前状态是否相等的函数,默认使用 defaultEqualityFn
2.初始化状态
const [state, setState] = useState(() => selector(store.getState()))
- 使用
useState来创建一个状态state,通过selector(store.getState())初始化状态 selector是从全局状态store中选择的状态部分
3.更新 selector 和 equalityFn 引用
useEffect(() => {
latestSelector.current = selector
latestEqualityFn.current = equalityFn
})
- 该
useEffect每次selector或equalityFn改变时会更新latestSelector和latestEqualityFn的值 - 确保
checkForUpdate中始终使用最新的selector和equalityFn
4.监听状态变化并且更新组件状态
useEffect(() => {
const checkForUpdate = () => {
const newState = latestSelector.current(store.getState())
if (!latestEqualityFn.current(newState, latestState.current)) {
latestState.current = newState
setState(newState)
}
}
const unsubscribe = store.subscribe(checkForUpdate)
checkForUpdate()
return () => {
unsubscribe()
}
}, [])
- 第二个
useEffect负责订阅全局store的状态变化,并在状态变化时更新组件的状态 checkForUpdate用于检查状态是否发生变化,它通过latestSelector.current(store.getState())获取新的状态,并与latestState.current进行比较- 如果新状态与当前状态不同,则更新组建的状态(调用
setState(newState)),并更新latestState.current为新状态 store.subscribe(checkForUpdate)使得当全局状态变化时,checkForUpdate会被触发。返回的unsubscribe用于在组件卸载时取消订阅
总结:
- 这个
useSelectorHook 允许你从全局 store 中选择并订阅某一部分状态。 - 它通过 selector 函数从 store 中选择状态部分,通过
equalityFn函数比较新旧状态是否相等,避免不必要的重新渲染。 - 通过
useEffect和store.subscribe机制,组件在状态变化时能够自动更新,从而确保 UI 与全局状态保持同步。
使用场景:
const hasAuthorisePermission = useSelector((state) => {
const permissions = state.local.data.permissions
return permissions.find((el) => el.permissionId === Permissions.AML_AUTHORISE && el.grantedRevoked === 'G') !== undefined
})
或
const customerId = useSelector((state) => {
const customer = state.local.data.customer
return customer?.customerId
})
useLocalStorage
这个自定义 Hook 用于与 localStorage 交互,实现数据的持久化存储。
import { useState } from 'react';
// 自定义 Hook:useLocalStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Could not save to localStorage', error);
}
};
return [storedValue, setValue];
}
// 使用自定义 Hook 的组件
function App() {
const [name, setName] = useLocalStorage('name', 'John Doe');
return (
<div>
<h1>Hello, {name}</h1>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
}
export default App;
解析:
useLocalStorage是一个自定义 Hook,它将localStorage中的值和组件的状态相结合,确保在浏览器刷新后仍能保留状态。- 它通过
JSON.parse和JSON.stringify处理数据的序列化和反序列化。
setValue 是自定义 Hook useLocalStorage 中定义的一个函数,它会被传递给使用该 Hook 的组件作为返回值的一部分。setValue 是在 App 组件的 onChange 事件处理程序中被调用的。
关于 setValue 的调用时机
useLocalStorage返回 setValue:在useLocalStorage中,setValue是作为返回值的一部分与storedValue一起返回。具体来说,useLocalStorage返回一个数组,数组的第一个元素是从localStorage读取的storedValue(或者是初始值),第二个元素是setValue,它是用来更新storedValue和localStorage的函数。
return [storedValue, setValue];
setName(即setValue)被调用:在 App 组件中,useLocalStorage被调用,返回了一个数组,数组的第一个元素name是storedValue,第二个元素setName是setValue。然后,setName(即setValue)被用作输入框的onChange事件处理程序。
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)} // setName 被调用
/>
onChange事件触发:当用户在输入框中输入内容时,onChange事件会被触发,并且事件对象e会传递给处理函数。在事件处理函数中,调用了setName(e.target.value),也就是setValue(e.target.value)。
4.setValue 的执行:
setValue函数会被调用,value是输入框的最新值(e.target.value)。setStoredValue(value)更新了storedValue(即name),它会触发组件的重新渲染。- 同时,
window.localStorage.setItem(key, JSON.stringify(value))将新的值存储到localStorage中,这样即使刷新页面,新的值仍然会被保留。