React Hooks
2020-04-01 本文已影响0人
zhenghongmo
1、useState(使用状态)
- 不可局部更新
如果state是一个对象,不可部分setState,因为setState不会帮我们合并属性
const [user,setUser] = useState({name:'xxx', age: 20})
setUser({
name: 'yyyyy'
}) //这样user中的age就不会保存,只留下name
-
地址要变
setState(obj) 如果 obj 地址不变,那么React就认为数据没有变化 -
useState 接受函数
const [state, setState] = useState(()=>{
return initialState
})
//该函数返回初始state,且只执行一次
- setState 接受函数
const [n, setN] = useState(0)
---------------------
onClick = () => {
setN(n+1)
setN(n+1)
}
n 并不能加 2
--------------------
onClick = () => {
setN(i=>i+1)
setN(i=>i+1)
}
但是这样n就可以加2
2、useReducer
- 创建初始值 initialState
- 创建所有操作 reducer(state, action)
- 传给useReducer,得到读和写API
- 调用 dispatch({ type: "操作类型"});
const initial = {
n: 0
};
const reducer = (state, action) => {
if (action.type === "add") {
return { n: state.n + action.number };
} else if (action.type === "multi") {
return { n: state.n * action.number };
} else {
throw new Error("unknown type");
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
return (
<div className="App">
<button onClick={onClick}>+1</button>
</div>
);
}
3、useContext(上下文)
- 使用
mycontext = createContext(initial)
创建上下文 - 使用
<mycontext.provider>
圈定作用域 - 在作用域内使用
useContext(mycontext)
来使用上下文
const mycontext = createContext(null);
function App() {
const [n, setN] = useState(0);
return (
<mycontext .Provider value={{ n, setN }}> //value把值传过来,在此作用域中的都可以用此值
<div className="App">
<Child/>
</div>
</mycontext .Provider>
);
}
function Child() {
const { n, setN } = useContext(mycontext); //取到父组件中的useState
return (
<div>
n: {n}
</div>
);
}
4、useEffect(副作用)
- 每次render后运行,在浏览器渲染完成后执行
- 作为componentDidMount使用,[ ]作为第二个参数
- 作为componentDidUpdate使用,可以在[ ]中制订以来
- 作为componentWillUnmount使用,通过return
- 如果同时存在多个useEffect,会按照出现次序执行
5、useLayoutEffect(布局副作用)
- 在浏览器渲染之前执行
- useLayoutEffect总是比useEffect先执行
6、 useMemo => useCallback
-
React.memo(child)
用React.memo包裹后,组件如果props不变,就不会再执行一次函数组件 - useMemo第一个参数是()=> value,第二个参数是依赖[m,n], 只有当依赖变化时,才会重新计算出新的value,如果依赖不变就重用之前的value(跟vue2的computed相似)
-
useCallback(x => console.log(x), [m])
等价于useMemo(() => x=> console.log(x), [m])
function App() {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
const onClick = () => {
setN(n + 1);
};
const onClickChild = useMemo(() => {
const fn = div => {
console.log(div);
};
return fn;
}, [m]);
return (
<div className="App">
<div>
<button onClick={onClick}>update n {n}</button> //点击后n变化了,但是m没有变
</div>
<Child data={m} onClick={onClickChild}/ > //就算m没有变化,Child组件也会执行,但如果用React.memo(Child)后,m不变就不会再执行一次Child组件,如果添加一个监听函数(例如onClick)就算m不变还是一样会执行,这时候就要用useMemo
</div>
);
}
function Child(props) {
console.log("child 执行了");
console.log('假设这里有大量代码')
return <div onClick={e => props.onClick(e.target)}>child: {props.data}</div>;
}
const Child2 = React.memo(Child);
7、useRef
- 在组件不断render时保持不变
- 初始化 const count = useRef(0)
- 读取 count.current
8、自定义hooks
import { useState, useEffect } from "react";
const useList = () => {
const [list, setList] = useState(null);
useEffect(() => {
ajax("/list").then(list => {
setList(list);
});
}, []); // [] 确保只在第一次运行
return {
list: list,
setList: setList
};
};
export default useList;
function ajax() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{ id: 1, name: "Frank" },
{ id: 2, name: "Jack" },
{ id: 3, name: "Alice" },
{ id: 4, name: "Bob" }
]);
}, 2000);
});
}
-------------------------------------------
在别的组件中可以使用这个hooks
import useList from "./hooks/useList";
const { list, setList } = useList();