Redex-Hooks
Hook API是React 16.8的新特性, 可以让我们可以在函数组件中使用state及其它特性.
基础Hook
useState
生成state值与修改state值的函数, 传入默认值, 返回值与操作函数.
使用方式: const [state, setState] = useState(initialState);
useEffect
用于模拟类组件的componentDidMount
、componentDidUpdate
和componentWillUnmount
生命周期
useEffect传入两个参数, 第一个参数传入副作用函数, 模拟componentDidMount
、componentDidUpdate
, 函数再返回的函数则相当于生命周期componentWillUnmount
执行.
第二个参数传入参数组成的数组, 当值不同时才执行副作用函数, 当传入[]时表示副作用只执行一次.
使用如下:
useEffect(
() => {
something();
return () => {
something();
}
},
[a]
)
useContext
用于返回祖先组件最近的 <MyContext.Provider>
的 value prop
值
使用方式: const theme = useContext(ThemeContext);
示例
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
额外的 Hook
useReducer
useState的替代方案, 有点像redux的理念, 使用时传入三个参数, 返回state值与操作函数.
使用方式: const [state, dispatch] = useReducer(reducer: (state: T, action: {}) => T, initialArg: T, init: (state: T) => T);
示例:
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useCallback
用于比对传入的数据值, 值变更时执行副作用
使用方式: const memoizedCallback = useCallback(callback: () => void, [params]);
示例(测量 DOM 节点):
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
将示例抽象成自定义Hook
function MeasureExample() {
const [rect, ref] = useClientRect();
return (
<>
<h1 ref={ref}>Hello, world</h1>
{rect !== null &&
<h2>The above header is {Math.round(rect.height)}px tall</h2>
}
</>
);
}
function useClientRect() {
const [rect, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
useMemo
记住上一次的计算结果, 当传入的依赖数组值没有发生变化则返回上一次返回的值
使用方式: const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
示例:
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
useRef
返回一个引用对象, 传入初始值, 值保存在Ref引用的current中
使用方式: const refContainer: { current: any } = useRef(initialValue: any);
示例(使用useRef获取上一次渲染的state):
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return <h1>Now: {count}, before: {prevCount}</h1>;
}
将示例抽象成自定义Hook
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return <h1>Now: {count}, before: {prevCount}</h1>;
}
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
useImperativeHandle
用于将子组件的引用暴露给父组件使用, 需要和forwardRef配合使用
示例:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useLayoutEffect
使用方式与useEffect
相同, 唯一区别是, useEffect
在DOM渲染结束后执行, 而useLayoutEffect
与DOM渲染同步执行
useDebugValue
useDebugValue 用于在 React 开发者工具中显示自定义 hook 的标签