Hook是React16.8的一个新增项,可以在不编写class的情况下使用state以及其他的React特性.
从react中导出useState函数
import React,{useState} from 'react';
useState(initialState),接收初始状态,返回一个状态变量和其更新函数.在更新函数中传入新的值可以变更状态的值.
useEffect会在每次渲染时执行,可以有多个useEffect函数.
useEffect(() => {
// Update the document title using the browser API
document.title = `您点击了 ${count} 次`;
},[]);
可以传递第二个参数为空数组[],这样就只会执行一次类似于componentDidMount
useEffect(() => {
// Update the document title using the browser API
document.title = `您点击了 ${count} 次`;
},[count]);
第二个参数可以在数组中传入状态,这样useEffect只有在数组中的状态的值发生变化时才会执行.数组中可以有多个状态
useEffect(()=>{
return () => {};
});
每当用户点击一次按钮 都会重新触发render函数,每次render拿到的都是独立的状态
因为我们生命count的值时使用const,所以每次渲染拿到的count值是一个独立的常量
function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
每次count值改变,都会触发render,组件重新渲染,所以每次都会生成对应的useEffect函数
而且我们发现每次打印count的值拿到的都是当前轮次的count值(并不是最新的count)
function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
// 假设在1s内多次点击按钮 这里打印的count值是什么?
useEffect(() => {
setTimeout(() => {
console.log(count) // 这里打印的会是当前这一次的count值,并不是最新的count值
}, 1000)
})
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
我们知道每次渲染都会触发render,每次更新就会生成一个新的Effect函数,并且每一个Effect函数里面都有独立的State,且只能访问自己本次更新的State。
所以用上面的例子,得出的结论就是:count值其实不是在同一个Effect函数里面发生改变,而是每一次的组件更新,都会生成一个维护着本次更新的Effect函数,在这个最新的Effect函数里就可以访问到最新的count值。
function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
useEffect(() => {
let timer = setTimeout(() => {
console.log(count)
}, 1000)
// 清理工作
return () => {
clearTimeout(timer)
}
})
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
假设用户点击了两次次按钮 当第一次点击的时候 count + 1 = 1,然后执行clearTimout清除本次的定时器?
接着继续count + 1 = 2 然后执行clearTimeout清除本次的定时器?正确的顺序应该是:当第一次点击 count + 1 = 1,然后clearTimeout会被延迟执行,等到第二次点击的时候 count +1 = 2 再执行上一次的clearTimeout 然后以此类推…问题来了不是说effect函数只能访问本次的State吗?那它怎么拿到上一次的clearTimeout并执行的?
其实很简单,就是React会帮你记住每次effect函数的State(包括清除函数),它确实是只能读取本次更新的State,只不过是延迟执行了(把清除函数的执行时机放在DOM渲染完成后,在下一次render触发之前)
自定义Hook是一个函数,名称用"use"开头,函数内部可以调用其他钩子
function useAge(){
const [age,setAge] = useState(0);
useEffect(()=>{
setTimeout(()=>{
setAge(20);
},2000);
});
return age;
}
export default function HooksTest() {
// useState(initialState),接收初始状态,返回一个状态变量和其更新函数
const age = useAge();
return (
<div>
<p>{age?age:"loading..."}</p>
</div>
);
}
useContext,useReducer,useCallback,useMemo
上下文提供一种不需要每层设置props就能跨多级组件传递数据的方式
import React, { useContext } from "react";
// 1.创建上下文
const MyContext = React.createContext();
const { Provider, Consumer } = MyContext;
function Child(prop) {
return <div>Child: {prop.foo}</div>;
}
// 使用hook消费
function Child2() {
const context = useContext(MyContext);
return <div>Child2: {context.foo}</div>;
}
// 使用class指定静态contextType
class Child3 extends React.Component {
// 设置静态属性通知编译器获取上下文中数据并赋值给this.context
static contextType = MyContext;
render() {
return <div>Child3: {this.context.foo}</div>
}
}
export default function ContextTest() {
return (
<div>
<Provider value={{ foo: "bar" }}>
{/* 消费方法1:Consumer */}
<Consumer>{value => <Child {...value} />}</Consumer>
{/* 消费方法2:hook */}
<Child2 />
{/* 消费方法3:contextType */}
<Child3 />
</Provider>
</div>
);
}