少停
>
React学习:React_Hooks学习
今天尝试下React Hooks
的学习.
什么是React Hooks? React Hooks和传统的开发模式有何不同? 使用React Hooks有哪些好处?
什么是React Hooks?
官方解释:Hook 是 React 16.8 的新增特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,如:保存状态、保存上下文、保存引用等.Hook 本质就是 JavaScript 函数.说白了就是对于函数组件的加强,增加了对于函数组件的一些特性,像class组件那样使用,是一套全新的API或者设计思想.
它和传统开发模式有何异同?
官方推荐使用钩子(函数),而不是类。因为钩子更简洁,代码量少,用起来比较"轻",而类比较"重"。而且,钩子是函数,更符合 React 函数式的本质。 类(class)是数据和逻辑的封装。 也就是说,组件的状态和操作方法是封装在一起的。如果选择了类的写法,就应该把相关的数据和操作,都写在同一个 class 里面。
Hook 这个单词的意思是"钩子"。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。你需要什么功能,就使用什么钩子。React 默认提供了一些常用钩子,你也可以封装自己的钩子。所有的钩子都是为函数引入外部功能,所以 React 约定,钩子一律使用use前缀命名,便于识别。你要使用 xxx 功能,钩子就命名为 usexxx。
Redux 的作者 Dan Abramov 总结了组件类的几个缺点。
1 2 3 4 5 大型组件很难拆分和重构,也很难测试。 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
吹得都上天了,其实 御姐小萝莉,各有所爱,各有所长.据官方资料来说:
1 2 3 4 5 6 7 完全可选的。 你无需重写任何已有代码就可以在一些组件中尝试 Hook。但是如果你不想,你不必现在就去学习或使用 Hook。 100% 向后兼容的。 Hook 不包含任何破坏性改动。 现在可用。 Hook 已发布于 v16.8.0。 没有计划从 React 中移除 class。 Hook 不会影响你对 React 概念的理解。 恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。
实操一把
准备工作:
1、react版本需 16.8.0以上.
2、使用 create-react-app
或 npm
或yarn
等方式新建一个最基本的项目即可,如:npx create-react-app my-app
传统模式-->Example-class.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import React,{Component} from 'react' // class的基本演示 class ExampleClass extends Component { constructor(props){ super(props); this.state = {count:0} } // 组件第一次执行 componentDidMount(){ console.log('ExampleClass执行了') } render(){ return ( <div> <p>class简单使用</p> <p>{this.state.count}</p> <button onClick={()=> {this.setState({count:this.state.count+1})}}>+1</button> <button onClick={()=> {this.setState({count:this.state.count-1})}}>-1</button> </div> ) } } export default ExampleClass;
useState事例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import React, { useState,useEffect } from 'react' // useState 简单使用 function Example1() { const [count, setCount] = useState(0); // 此处的useEffect相当于componentDidMount 和 componentDidUpdate useEffect(()=>{ console.log('Example1下 useEffect执行了'); }) return <div> <p>useState的简单使用</p> <p> {count}</p> <button onClick={() => { setCount(count + 1) }}>+1</button> <button onClick={() => { setCount(count - 1) }}>-1</button> </div> } export default Example1;
useEffect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import React, { useState ,useEffect} from 'react' function Example2() { const [count, setCount] = useState(0); const [color, setColor] = useState('red'); // 此处的useEffect相当于componentDidMount 和 color的componentDidUpdate useEffect(()=>{ console.log('------切换color--------'); },[color]) // 此处的useEffect相当于componentDidMount 和 count的componentDidUpdate useEffect(()=>{ console.log('点击count'); },[count]) function changeColor(){ if(color === 'yellow'){ setColor('red'); }else{ setColor('yellow'); } } return <div style={{background:color}}> <p>useEffect简单使用</p> <p> {count}</p> <button onClick={() => { setCount(count + 1) }}>+1</button> <button onClick={() => { setCount(count - 1) }}>-1</button> <button onClick={() => changeColor()}>切换颜色</button> </div> } export default Example2;
useEffect代替componentWillUnmount使用(需要安装react-router-dom):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import React, { useEffect } from 'react' import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; function Index1() { useEffect(() => { console.log('组件1componentDidMount '); return () => { console.log('组件1componentWillUnmount'); } }) return <div> <h2>组件1</h2> </div> } function Index2() { useEffect(() => { console.log('组件2componentDidMount '); return () => { console.log('组件2componentWillUnmount'); } }) return <div> <h2>组件2</h2> </div> } function Example3() { return <div> <p>useEffect代替componentWillUnmount使用</p> <Router> <ul> <li><Link to="/">组件1</Link></li> <li><Link to="/two/">组件2</Link></li> </ul> <Route path="/" exact component={Index1} /> <Route path="/two/" component={Index2} /> </Router> </div> } export default Example3;
useContext的简单使用:父子之间传递数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import React, { useState,createContext,useContext } from 'react' // useState 简单使用 function Example4() { const [count, setCount] = useState(0); const CountContext = createContext(); function Child(){ let count = useContext(CountContext); return ( <h1>我是子组件{count}</h1> ) } return <div> <p>useContext的简单使用:父子之间传递数据</p> <p>我是父组件: {count}</p> <button onClick={() => { setCount(count + 1) }}>+1</button> <button onClick={() => { setCount(count - 1) }}>-1</button> <CountContext.Provider value={count}> <Child /> </CountContext.Provider> </div> } export default Example4;
useRef使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import React, { useState,useRef } from 'react' // useRef 简单使用 function Example5() { const [inputVal,setInputVal] = useState('') const ref = useRef(); function inputAction (){ console.log(ref.current.value); setInputVal(ref.current.value); } return <div> <p>useRef使用:{inputVal}</p> <input onChange={inputAction} ref={ref} /> </div> } export default Example5;
useReducer的简单使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import React, { useReducer } from 'react'; const initialState = 0; const reducer = (state, action) => { switch (action) { case 'increment': return state + 1; case 'decrement': return state - 1; case 'reset': return 0; default: throw new Error('Unexpected action'); } }; const Example6 = () => { const [count, dispatch] = useReducer(reducer, initialState); return ( <div> <p>useReducer的简单使用</p> {count} <button onClick={() => dispatch('increment')}>+1</button> <button onClick={() => dispatch('decrement')}>-1</button> <button onClick={() => dispatch('reset')}>reset</button> </div> ); }; export default Example6;
useMemo提升性能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import React, { useState, useMemo } from 'react' // useState 简单使用 function Example7() { const [name, setName] = useState('name'); const [content, setContent] = useState('content'); return ( <div> <div>useMemo提升性能</div> <button onClick={() => setName(new Date().getTime())}>{name}</button> <button onClick={() => setContent(new Date().getTime())}>{content}</button> <Child name={name}>{content}</Child> </div> ) } // 不使用useMemo,会造成不必要的性能开销 // function Child({name,children}){ // function changeName(name){ // console.log('child执行了'); // return name + '改变'; // } // const otherName = changeName(name); // return ( // <div> // <div>{otherName}</div> // <div>{children}</div> // </div> // ) // } // 使用useMemo function Child({ name, children }) { function changeName(name) { console.log('child执行了'); return name + '改变'; } const otherName = useMemo(() => changeName(name),[name]) return ( <div> <div>{otherName}</div> <div>{children}</div> </div> ) } export default Example7;
自定义Hook函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import React, { useState,useEffect,useCallback } from 'react' function useWinSize(){ const [size,setSize] = useState({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight, }) const onResize = useCallback(()=>{ setSize({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight, }) },[]) useEffect(()=>{ window.addEventListener('resize',onResize); return ()=>{ window.removeEventListener('resize',onResize); } }) return size; } function Example8() { const size = useWinSize(); return <div> <p>自定义Hook函数</p> <p>页面size:{size.width} + {size.height}</p> </div> } export default Example8;
ps:入口文件就只是引入而已.index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import Example1 from './Example1'; import Example2 from './Example2'; import Example3 from './Example3'; import Example4 from './Example4'; import Example5 from './Example5'; import Example6 from './Example6'; import Example7 from './Example7'; import Example8 from './Example8'; import ExampleClass from './Example-class'; ReactDOM.render( <div> {/* <ExampleClass /> */} <Example1 /> <hr/> <Example2 /> <hr/> <Example3/> <hr/> <Example4/> <hr/> <Example5/> <hr/> <Example6/> <hr/> <Example7/> <hr/> <Example8/> </div>, document.getElementById('root') );
React Hooks 的优点
1 2 3 4 5 6 7 8 通过 Hooks 我们可以对 state 逻辑进行良好的封装,轻松做到隔离和复用,优点主要体现在: 复用代码更容易:hooks 是普通的 JavaScript 函数,所以开发者可以将内置的 hooks 组合到处理 state 逻辑的自定义 hooks中,这样复杂的问题可以转化一个单一职责的函数,并可以被整个应用或者 React 社区所使用; 使用组合方式更优雅:不同于 render props 或高阶组件等的模式,hooks 不会在组件树中引入不必要的嵌套,也不会受到 mixins 的负面影响; 更少的代码量:一个 useEffect 执行单一职责,可以干掉生命周期函数中的重复代码。避免将同一职责代码分拆在几个生命周期函数中,更好的复用能力可以帮助优秀的开发者最大限度降低代码量; 代码逻辑更清晰:hooks 帮助开发者将组件拆分为功能独立的函数单元,轻松做到“分离关注点”,代码逻辑更加清晰易懂; 单元测试:处理 state 逻辑的自定义 hooks 可以被独立进行单元测试,更加可靠;
整理自:
React Hooks完全上手指南-蚂蚁 RichLab 前端团队 React Hooks 入门教程-阮一峰 轻松学会 React 钩子:以 useEffect() 为例-阮一峰 bilibili 本文源码