这是官网的解释:_Hook_ 是 React 16.8 的新增特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。html
阮一峰大神的解释:React Hooks 的意思是,组件尽可能写成纯函数,若是须要外部功能和反作用,就用钩子把外部代码"钩"进来。node
个人理解:Hook是能够提供给咱们更加简洁的方式去使用React的其余特性。react
可使用 Hook 从组件中提取状态逻辑,使得这些逻辑能够单独测试并复用。Hook 使你在无需修改组件结构的状况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。编程
咱们常常维护一些组件,组件起初很简单,可是逐渐会被状态逻辑和反作用充斥。为了解决这个问题,Hook 将组件中相互关联的部分拆分红更小的函数(好比设置订阅或请求数据),而并不是强制按照生命周期划分。你还可使用 reducer 来管理组件的内部状态,使其更加可预测。json
Hook 使你在非 class 的状况下可使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。api
const [state, setState] = useState(initialState);
返回值:state,更新state的函数setState数组
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次从新渲染加入队列。setState(newState);浏览器
React 会确保 setState 函数的标识是稳定的,而且不会在组件从新渲染时发生变化。性能优化
参数:初始的state:initialStatedom
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
示例代码:
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> </> ); }
注意:与 class 组件中的 setState 方法不一样,useState 不会自动合并更新对象。你能够用函数式的 setState 结合展开运算符来达到合并更新对象的效果。
setState(prevState => { // 也可使用 Object.assign return {...prevState, ...updatedValues}; });
该Hook接收一个包含命令式、且有可能有反作用代码的函数。
useEffect(didUpdate); //didUpdate指的是包含命令式、且有可能有反作用代码的函数
在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其余包含反作用的操做都是不被容许的,由于这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
使用 useEffect 完成反作用操做。赋值给 useEffect 的函数会在组件渲染到屏幕以后执行。你能够把 effect 看做从 React 的纯函数式世界通往命令式世界的逃生通道。
默认状况下,effect 将在每轮渲染结束后执行,但你能够选择让它 在只有某些值改变的时候 才执行。
[](https://zh-hans.reactjs.org/d...,组件卸载时须要清除 effect 建立的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数。如下就是一个建立订阅的例子:
useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除订阅 subscription.unsubscribe(); }; });
默认状况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被从新建立。
然而,在某些场景下这么作可能会矫枉过正。好比,在上面的例子中,咱们不须要在每次组件更新时都建立新的订阅,而是仅须要在 source prop 改变时从新建立。
要实现这一点,能够给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。更新后的示例以下:
useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], //第二个参数是Effect的依赖项,在这里,只有当该数组发生变化的时候,才去执行useEffect );
此时,只有当 props.source 改变后才会从新建立订阅。
const value = useContext(MyContext); //MYContext是建立好的context对象
注意:
1.useContext(MyContext) 只是让你可以_读取_ context 的值以及订阅 context 的变化。你仍然须要在上层组件树中使用 <MyContext.Provider> 来为下层组件_提供_ context。
示例代码:
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); //这里订阅到了provider传递下来的value值 return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); }
const [state, dispatch] = useReducer(reducer, initialArg, init);
它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
useReducer和useState的比较:这二者都是返回了新的状态和一个函数。在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于以前的 state 等。而且,使用 useReducer 还能给那些会触发深更新的组件作性能优化。
如下是用 reducer 重写 useState 一节的计数器示例:
const initialState = {count: 0}; function reducer(state, action) { //reducer switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
第三个参数:init
你能够选择惰性地建立初始 state。为此,须要将 init 函数做为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。
仍是上一个计数器的代码,这里使用了第三个参数添加了一个reset功能:
function init(initialCount) { //init函数:用于惰性建立初始state 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); //init函数做为第三个参数传入useReducer 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> </> ); }
const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
一个常见的用例即是命令式地访问子组件:
function TextInputWithFocusButton() { const inputEl = useRef(null); //建立一个ref对象 const onButtonClick = () => { // `current` 指向已挂载到 DOM 上的文本输入元素 inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> //将建立好的ref对象放进ref属性中 <button onClick={onButtonClick}>Focus the input</button> </> ); }
useRef和ref属性的区别:
注意:
当 ref 对象内容发生变化时,useRef 并_不会_通知你。变动 .current 属性不会引起组件从新渲染。若是想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则须要使用回调 ref 来实现。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
注意点:
与useEffect的区别:
在计算机科学中,函数反作用指当调用函数时,除了返回函数值以外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。
对于运行在浏览器的JS来讲,反作用包括且不限于:
上例的Hooks代码还能够封装起来,变成一个自定义的Hook,便于共享。
const usePerson = (personId) => { const [loading, setLoading] = useState(true); const [person, setPerson] = useState({}); useEffect(() => { setLoading(true); fetch(`https://swapi.co/api/people/${personId}/`) .then(response => response.json()) .then(data => { setPerson(data); setLoading(false); }); }, [personId]); return [loading, person]; };
上面代码中,usePerson()就是一个自定义的Hook。
Person组件就改用这个新的钩子,引入封装的逻辑。
const Person = ({ personId }) => { const [loading, person] = usePerson(personId); if (loading === true) { return <p>Loading ...</p>; } return ( <div> <p>You're viewing: {person.name}</p> <p>Height: {person.height}</p> <p>Mass: {person.mass}</p> </div> ); };
本文未完持续更新中...