在React函数组件中,是默认只有属性没有状态的,咱们能够使用以下代码用useState读写变量和设定初始值 :react
const [n,setN] = react.useState(0)
redux
const [user,setUser] = react.useState({name:'Jack'})
数组
💡 注意事项bash
咱们没法局部setState,由于react函数组件不会帮咱们合并属性。解决办法是使用react提供的...
语法将原属性拷贝并放在更新值以前。ide
由于setState(obj)
中obj
若是是在原来的对象上进行修改,那么对象的地址没有更改,react会将这个对象断定为原对象,则更改无效。解决方法是始终设置新的对象就能够了。函数
① useState的初始值并不局限于对象,能够用函数的形式设定。布局
const [state, setState] = useState(()=>{ return initialState })
ui
② 因为表达式的局限性,若是你须要在setState中进行屡次操做,那么须要使用函数的形式以确保每一次的操做是有效的。spa
const onClick = ()=>{
setN(n+1) //无效操做
setN(n+1) //你会发现 n 不能加 2
}
复制代码
const onClick = ()=>{
setN(i=>i+1) //有效操做
setN(i=>i+1) //最后值为n+2
}
复制代码
若是你发现有几个变量须要汇总在一块儿,你能够使用复杂版的useState —— useReducer,它的好处就是能够对这个对象进行一个总体的操做 。而且能够践行React社区一直推崇的Flux/Redux思想,在我看来这个思想可能会在不久的未来Hooks思想盛行以后逐渐被取代,可是目前仍是须要了解这个被普遍应用的Hook。翻译
使用useReducer分为四个步骤:
initialState
reducer(state,action)
useReducer
获得读和写API({type:"操做类型"})
一个🌰:
const initial = {
n:0
};
const reducer = (state,action) => {
if(action.type === "add") {
return { n:state.n + 1 };
} else if (action.type === "multi") {
return { n:state.n * 2 };
} else {
throw new Error("unknown type");
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
const onClick2 = () => {
dispatch({ type: "multi", number: 2 });
};
return (
<div className = "App">
<div>n: {n}</div>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>*2</button>
</div>
);
}
复制代码
如何代替redux?
这个Hook翻译过来就是“上下文”。何为上下文?上下文就是你在运行一个程序的时候所要知道的全部变量。全局变量就是全局的上下文,上下文是局部的全局变量。上一个Hook中咱们已经了解到useContext的用法。
一个🌰:
const C = createContext(null);
function App() {
const [n, setN] = useState(0);
return (
<C.Provider value={{ n, setN }}>
<div className="App">
<Dog />
</div>
</C.Provider>
);
}
function Dog() {
const { n, setN } = useContext(C);
return (
<div>
狗 n: {n} <Child />
</div>
);
}
function Child() {
const { n, setN } = useContext(C);
const onClick = () => {
setN(i => i + 1);
};
return (
<div>
狗儿子 n: {n}
<button onClick={onClick}>+1</button>
</div>
);
}
复制代码
这个Hook翻译过来为“反作用”,我认为把它叫作afterRender更好,由于这是每次render以后就会调用的一个函数。 用途:
做为componentDidMount(出生后)使用
useEffect(()=>{console.log('hi'),[]}) //使用空数组表示第一次渲染时
做为componentUpdate(更新)使用
useEffect(()=>{console.log('hi'),[n]}) //表示当{n}变化时
useEffect(()=>{console.log('hi')}) //表示每一次更新时
做为componentWillUnmount(将死)使用
useEffect(()=>{console.log('hi')})
return ()=>{} //添加return表示当组件将时执行某操做
复制代码
useLayoutEffect
因为react默认会有多余的render出现,若是props不变就没要再次执行这个函数组件,useMemo就是阻止函数组件无效执行的Hook。
❗❗ 若是在函数组件中添加一个监听事件,则useMemo会直接无效,须要写成:
useMemo(()=> (x) => console.log(x))
这种形式,一个返回函数的函数。 因为这种方式难用且难以理解,因而诞生了useCallback。
useCallback(x => log(x), [m]) //等价于下面一种写法
useMemo(() => x => log(x), [m])
复制代码
若是你须要一个值在组件不断render的时候保持始终是同一个值地址不变,那就须要用到useRef。
首先须要初始化:const n = useRef(0)
读取时则是:n.current
(这里的current是用来保证两次useRef的是相同的值的引用)
❗❗ useRef是不能作到变化的时候自动render的,由于这不符合react的理念,若是你想要这个功能,彻底能够本身加,监听ref,当ref.current变化的时候,调用setX便可。
自定义Hook必须以use开头,这是react既定的语法。经过自定义 Hook,能够将组件逻辑提取到可重用的函数中。
一个🌰:
function App() {
const { list } = useList();
return (
<div className="App">
<h1>List</h1>
{list ? (
<ol>
{list.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ol>
) : (
"加载中..."
)}
</div>
);
}
复制代码
与 React 组件不一样的是,自定义 Hook 不须要具备特殊的标识。咱们能够自由的决定它的参数是什么,以及它应该返回什么(若是须要的话)。换句话说,它就像一个正常的函数,咱们能够最大限度的灵活使用这个Hook。