- useState【维护状态】
- useEffect【副作用操作】
- useContext【使用共享状态】
- useReducer【类似redux】
- useCallback【缓存函数】
- useMemo【缓存值】
- useRef【访问DOM】
- useImperativeHandle【使用子组件暴露的值/方法】
- useLayoutEffect【完成副作用操作,阻塞浏览器绘制】
- umi - useRequest
useState
普通更新 / 函数式更新 state
useEffect
- 我依赖了某些值,但是我不要在初始化就执行回调方法,我要让依赖改变再去执行回调方法
- getData异步请求
- useCallback缓存,提高性能
useContext
组件共享此类值得方式,不必显式通过组件数传递props
useReducer
类似redux
useCallback
缓存函数,
react中只要父组件的 render 了,那么默认情况下就会触发子组
的 render,react提供了来避免这种重渲染的性能开销的一些方法:
避免必要得渲染,使用React.memo
Reace.memo只会对props做浅比较,也就是父组件重新render之后会传入
不同引用的方法 getList,浅比较之后不相等,导致子组件还是依然会渲染。
可以用useCallback解决
useMemo
不管render多少次,案例时间戳都不会改变,已经被缓存。缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态。
useRef
-
访问DOM,操作DOM,点击按钮聚焦
-
还有保持在ref对象在组件得整个生命周期内保持不变
-
操作组件得DOM,用到React.forwardRef高阶组件
useImperativeHandle
父组件调用到子组件暴露的属性/方法
-
vue类似的操作 this.$ref.xxx操作dom或者调用子组件的值/方法
useLayoutEffect
DOM变更之后同步调用 effect。同步刷新,阻塞浏览器绘制。
案例:
-
ant 有个Menu,在窗口缩小放大后tab会自动调整,用useEffect的话不阻塞绘制就会看起来效果不好,这里用了useLayoutEffect,ant.design/components/… 。不过这里resize的时候还有用到
requestAnimationFrame,合适的频率去刷新动画,节省CPU,函数节流。
参考案例:
import React, {
componentDidMount,
useState,
useEffect,
useRef,
useCallback,
useContext,
useMemo,
forwardRef,
useImperativeHandle
} from "react";
import "./style.css";
// useCallback React.PureComponent、React.memo ,shouldComponentUpdate() 的应用
const Index = () => {
const [count, setCount] = useState(0);
const getList = useCallback(n => {
return Array.apply(Array, Array(n)).map((item, i) => ({
id: i,
name: "张三" + i
}));
}, []);
return (
<>
<Child getList={getList} />
<button onClick={() => setCount(count + 1)}>count+1</button>
</>
);
};
const Child = React.memo(({ getList }) => {
console.log("child-render", getList(10));
return (
<>
{getList(10).map(item => (
<div key={item.id}>
id: {item.id}, name: {item.name}
</div>
))}
</>
);
});
// useRef
const Child1 = forwardRef((props, ref) => {
return <input ref={ref} />;
});
const Index1 = () => {
const inputEl = useRef(null);
const handleFocus = () => {
inputEl.current.focus();
};
// <Child1 ref={inputEl} />
return (
<>
<input ref={inputEl} type="text" />
<button onClick={handleFocus}>Focus</button>
</>
);
};
// useImperativeHandle
const Index2 = () => {
const inputEl2 = useRef();
useEffect(() => {
console.log(inputEl2.current.someValue);
}, []);
return (
<>
<Child2 ref={inputEl2} />
<button
onClick={() => {
inputEl2.current.setValue2(val => val + 1);
}}
>
累加子组件的Value
</button>
</>
);
};
const Child2 = forwardRef((props, ref) => {
const inputEl2 = useRef();
const [value2, setValue2] = useState(0);
useImperativeHandle(ref, () => ({
setValue2,
someValue2: "test"
}));
return (
<>
<div>child-value:{value2}</div>
<input ref={inputEl2} />
</>
);
});
export default function App() {
// useState
const [count, setCount] = useState(0);
const [obj, setObj] = useState({ id: 1 });
componentDidMount = () => {};
// useEffect
useEffect(() => {
console.log("init");
}, []);
// 初始化不执行,依赖改变了再去执行回调
const firstLoad = useRef(true);
useEffect(() => {
if (firstLoad.current) {
firstLoad.current = false;
return;
}
console.log("do something");
}, [obj]);
// 异步方法, 请求初始化后,点击按钮再度调用
// const getData = async () => {
// console.log("do something2");
// setCount(10);
// };
const [detail, setDetail] = useState({ id: 1 });
async function xxx({ id }) {
return {
id,
message: "ok"
};
}
const getData = useCallback(async () => {
console.log("do something2");
const d = await xxx({ id: 2 });
setDetail(d);
console.log("d", d);
}, []);
useEffect(() => {
console.log("useEffect getData", detail);
getData();
}, [getData]);
const handleClick = () => {
getData();
};
// useContext 共享主机值,不用逐层传递props
const obj1 = {
value: 1
};
const obj2 = {
value: 2
};
const Obj1Context = React.createContext(obj1);
const Obj2Context = React.createContext(obj2);
// 子级
const ChildComp = () => {
return <ChildChildComp />;
};
// 孙级或更多级
const ChildChildComp = () => {
const obj1 = useContext(Obj1Context);
const obj2 = useContext(Obj2Context);
return (
<>
<div>{obj1.value}</div>
<div>{obj2.value}</div>
</>
);
};
// useReducer
const initialState = [{ id: 1, name: "张三" }, { id: 2, name: "李四" }];
const reducer = (state, { type, payload }) => {
switch (type) {
case "add":
return [...state, payload];
defalt: throw new Error();
}
};
const List = () => {
const [state, dispatch] = userReducer(reducer, initialState);
return (
<>
List: {JSON.stringify(state)}
<Button
onClick={() =>
dispatch({
type: "add",
payload: {
id: 3,
name: "王五"
}
})
}
/>
</>
);
};
// useMemo
const getNumUseMemo = useMemo(() => {
return `${+new Date()}`;
}, []);
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<div>count: {count}</div>
<div>obj: {JSON.stringify(obj)}</div>
<button
onClick={() => {
setObj(prevObj => ({
...prevObj,
...{ id: 2, name: "test" }
}));
}}
>
merge
</button>
<hr />
<button onClick={handleClick}>getData</button>
<hr />
<Obj1Context.Provider value={obj1}>
<Obj2Context.Provider value={obj2}>
<ChildComp />
</Obj2Context.Provider>
</Obj1Context.Provider>
<Index />
<hr />
<Index1 />
{getNumUseMemo}
<hr />
<Index2 />
</div>
);
}
umi- useRequest
zhuanlan.zhihu.com/p/106796295
和apolle的useQuery钩子很像,其实也是加强了业务方面的能力
- 防抖+节流设置
- 多并发请求 loading延迟
- 分页?重置分页,重新发起请求
- 多tab同步操作
参考