关于React Hooks使用
目录
- 1 为什么使用 React Hooks
- 2 详解 useState
- 3 useEffect的使用
- 4 useContext的使用
- 5 useReducer的使用
- 6. useMemo
- 7. useRef
- 8. 自定义Hooks函数
1 为什么使用 React Hooks
对于一个新玩意(这也不算新玩意了)的使用肯定是有他的原因的撒,我们先来个简单例子看看,现在我们做过超级超级简单的例子:点击按钮数字增加1
先用我们原来的做法(用class的方式定义的组件)
import React, { Component } from 'react' class Increased extends Component { constructor (props) { super (props) this.state={count:0} } render() { return ( <div> <p>总数:{this.state.count}</p> <button onClick={this.add.bind(this)}>增加</button> </div> ) } add() { this.setState({count:this.state.count+1}) } } export default Increased
咋们在来看看使用React Hooks做的
import React,{useState } from 'react' const IncreasedHooks = () => { const [ count , setCount ] =useState(0)//数组解构 return ( <div> <p>总数:{count}</p> <button onClick={()=>setCount(count+1)}>增加</button> </div> ) } export default IncreasedHooks
下面是效果:
2 详解 useState
useState
是react自带的一个hook函数,它的作用是用来声明状态变量。useState这个函数接收的参数是状态的初始值
,它返回一个数组
,这个数组的第0位是当前的状态值
,第1位是可以改变状态值的方法函数
- 如何声明 根据上面的代码就知道啦
const [ count , setCount ] =useState(0)//数组解构
- 如何读取
因为返回的是个数组,按照取数组的元素的方式取就行了啦,读取是很简单的。只要使用{count}就可以,因为这时候的count就是JS里的一个变量,想在JSX中使用,值用加上{}就可以。
建议使用解构的方式,简单快速,什么? 你不会解构? 那你戳这个吧解构赋值这些些你必须知道
<p>总数:{count}</p>
- 使用 改变state 我们就要使用返回的数组中的第二个值啦
<button onClick={()=>setCount(count+1)}>增加</button>
3 useEffect的使用
3.1useEffect代替常用的生命周期函数
useEffect
可以用来代替我们常用的声明周期函数 ,那我们一般什么时候使用生命周期函数呢,当然是我们在是做"副作用"的业务处理 代替了componentDidMount
和componentDidUpdate
。分别在组件第一次渲染后在浏览器控制台打印出计数器结果和在每次计数器状态发生变化后打印出结
使用useEffect时候有两点需要注意的
-
React首次渲染和之后的每次渲染都会调用一遍useEffect函数,而之前我们要用两个生命周期函数分别表示首次渲染(componentDidMonut)和更新导致的重新渲染(componentDidUpdate)。
-
useEffect中定义的函数的执行不会阻碍浏览器更新视图,也就是说这些函数时异步执行的,而
componentDidMonut
和componentDidUpdate
中的代码都是同步执行的。个人认为这个有好处也有坏处吧,比如我们要根据页面的大小,然后绘制当前弹出窗口的大小,如果时异步的就不好操作了。
const IncreasedHooks = () => { const [ count , setCount ] =useState(0) useEffect(()=>{ console.log(`useEffect=>You clicked ${count} times`) }) //解决生命周期函数 代替了componentDidMount和componentDidUpdate。分别在组件第一次渲染后在浏览器控制台打印出计数器结果和在每次计数器状态发生变化后打印出结 return ( <div> <div>使用React Hooks</div> <p>总数:{count}</p> <button onClick={()=>setCount(count+1)}>增加</button> </div> ) }
3.2 实现类似componentWillUnmount(组件将要被卸载时执行)
使用路由实现组件的解绑,需要用到useEffect
函数里面返回一个函数的形式,代替解绑生命周期函数 componentWillUnmount 组件将要被卸载时执行
const Index = () => { useEffect(()=>{ console.log('useEffect=>老弟你来了!Index页面') return ()=>{ console.log('老弟,你走了!Index页面') }//返回一个函数的形式,代替解绑生命周期函数 componentWillUnmount 组件将要被卸载时执行 },[]) return <div>加油,程序员</div> } const List = () =>{ return ( <ul> <li>你好</li> <li>我好</li> <li>他好</li> </ul> ) } const IncreasedHooks = () => { return ( <div> <Router> <ul> <li><Link to = "/">首页 </Link></li> <li><Link to = "/list/">列表页 </Link></li> </ul> <Route path ="/" exact component={Index}></Route> <Route path ="/list/" component={List}></Route> </Router> </div> ) }
其实这个主要是使用的useEffect
的第二个参数,上面的程序中,不是用第二个参数的时候.每次状态发生变化,useEffect
都进行了解绑。真正实现主要是第二个人函数加了空数组.useEffect的第二个参数,它是一个数组,数组中可以写入很多状态对应的变量,意思是当状态值发生变化时,我们才进行解绑。但是当传空数组[]时,就是当组件将被销毁时才进行解绑,这也就实现了componentWillUnmount的生命周期函数。
我的理解是:第二个参数是实现解绑条件
例如:给计数器也加上解绑:只需要在返回的数组中写入记录计数的状态值count 变量
const IncreasedHooks = () => { const [ count , setCount ] =useState(0)//数组解构 useEffect(()=>{ console.log(`useEffect=>You clicked ${count} times`) return ()=>{ console.log('====================') } },[count]) return ( <div> <p>总数:{count}</p> <button onClick={()=>setCount(count+1)}>增加</button> </div> ) }
4 useContext的使用
useContext
主要是用来实现父子组件之间的传值 如下代码实现
import React,{useState ,useContext, createContext } from 'react' const CountContext = createContext() // 定义子组件 const Coounter = () =>{ //子组件一句话就可以得到父组件传递过来的count const count = useContext(CountContext) return (<h2>{count}</h2>) } // 父组件 const IncreasedHooks2= () => { const [ count , setCount ] =useState(0) return ( <div> <div>使用React Hooks</div> <p>总数:{count}</p> <button onClick={()=>setCount(count+1)}>增加</button> {/* 父组件向组件提供值 */} <CountContext.Provider value={count} > <Coounter/> </CountContext.Provider> </div> ) } export default IncreasedHooks2
5 useReducer的使用
5.1 useReducer时实现reducer
useContext
和useReducer
合作可以完成类似的Redux库的操作,useReducer
可以让代码具有更好的可读性和可维护性,它类似于Redux中的reducer
,reducer
这个函数接收两个参数,一个是状态,一个用来控制业务逻辑的判断参数
一个简单reducer的例子来理解什么是reducer
function countReducer(state, action) { switch(action.type) { case 'add': return state + 1; case 'sub': return state - 1; default: return state; } }
使用useReducer
import React, { useReducer } from 'react'; const IncreasedHooks2 = () => { const [count, dispatch] = useReducer((state, action) => { switch (action) { case 'add': return state + 1 case 'sub': return state - 1 default: return state } }, 0) return ( <div> <h2>现在的分数是{count}</h2> <button onClick={() => dispatch('add')}>Increment</button> <button onClick={() => dispatch('sub')}>Decrement</button> </div> ) } export default IncreasedHooks2
5.2 useReducer useContext实现redux的状态管理和状态共享
实现状态全局化并能统一管理,统一个事件的派发
案例:点击按钮切换对应的字体颜色
//父组件 import React from 'react'; import Buttons from './Buttons'; import ShowArea from './ShowArea' import { Color } from './Color'; //引入Color组件 const ChangeColor = () => { return ( <div> <Color> <Buttons /> <ShowArea /> </Color> </div> ) } export default ChangeColor //字体展示组件 import React,{useContext} from 'react' import { ColorContext } from './Color'; const ShowArea = () => { // 获取color const {color} = useContext(ColorContext) return ( <div> <div style={{color:color}}>字体颜色为{color}</div> </div> ) } export default ShowArea //按钮组件 import React ,{useContext} from 'react'; import {ColorContext,UPDATE_COLOR} from './Color' const Buttons = () => { // 获取共享的dispatch const {dispatch} = useContext(ColorContext) return ( <div> {/* 使用dispatch派发一个action */} <button onClick= {()=> {dispatch({type:UPDATE_COLOR,color:"red"})}}>红色</button> <button onClick= {()=> {dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黄色</button> </div> ) } export default Buttons //状态管理 import React,{createContext ,useReducer } from 'react' export const ColorContext = createContext() export const UPDATE_COLOR = "UPDATE_COLOR" // 定义reducer const reducer = (state, action) => { switch (action.type) { case UPDATE_COLOR: return action.color default: return state } } // 颜色共享 export const Color = props => { // 使用reducer const [color, dispatch] = useReducer(reducer,'red') return ( <div> {/* 将color和dispatch共享出去 */} <ColorContext.Provider value={{color,dispatch}}> {props.children} </ColorContext.Provider> </div> ); }
结果
6. useMemo
useMemo
主要用来解决使用React hooks产生的无用渲染的性能问题,函数型组件没有shouldCompnentUpdate
(组件更新前触发),我们就没有办法通过组件前的条件来决定组件是否更新.
且在函数组件中,也不再区分mount
和update
两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemo
和useCallback
都是解决上述性能问题的
import React , {useState,useMemo} from 'react'; function ComeHere(){ const [he, setHe] = useState('他在等着') const [me, setMe] = useState('我在等着') return ( <> <button onClick={()=>{setHe(new Date().getTime())}}>他</button> <button onClick={()=>{setMe(new Date().getTime()+',我走来了')}}>我</button> <ChildComponent name={he}>{me}</ChildComponent> </> ) } function ChildComponent({name,children}){ function changeHe(name){ console.log('她来了,她来了。他向我们走来了') return name+',他向我们走来了' } //为了解决当我们点击"我"按钮时,"他"对应的changeHe方法不能执行,只有在点击他按钮时才能执行。才能减少子组件的多次没有用的重新渲染 //其实只要使用useMemo,然后给她传递第二个参数,参数匹配成功,才会执行。 const actionHe = useMemo(()=>changeHe(name),[name]) return ( <> <div>{actionHe }</div> <div>{children}</div> </> ) }
7. useRef
-
用
useRef
获取React JSX中的DOM元素,获取后你就可以控制DOM的任何东西了。但是一般不建议这样来作,React界面的变化可以通过状态来控制。 -
用
useRef
来保存变量,这个在工作中也很少能用到,我们有了useContext这样的保存其实意义不大
import React, { useRef} from 'react'; function Example(){ //声明一个input的element const inputEl = useRef(null) const onButtonClick=()=>{ inputEl.current.value="Hello ,JSPang" console.log(inputEl) //输出获取到的DOM节点 } return ( <> {/*保存input的ref到inputEl */} <input ref={inputEl} type="text"/> <button onClick = {onButtonClick}>在input上展示文字</button> </> ) } export default Example
8. 自定义Hooks函数
实例,自第一个实时监测浏览器窗口大小的Hooks函数
自定义Hooks函数,记住一定要用use开头
import React,{ useState ,useEffect ,useCallback } from 'react'; const useWinSize = () =>{ const [size,setSize] = useState({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight }) //useCallback,目的是为了缓存方法(useMemo是为了缓存变量) const onResize = useCallback(() => { setSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }) },[]) useEffect(()=>{ window.addEventListener('resize',onResize) return ()=>{ window.removeEventListener('resize',onResize) } },[]) return size } //组件中使用 const MyHooks = ()=>{ const size = useWinSize() return <div>size:{size.width}x{size.height}</div> } export default MyHooks