React16.8的新增特性,Hook讲解

2020-06-07 13:09 插件 424 0
Hook是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性1.useState用来声明state变量,类似于class里的this.state,使用方法如下exportdefaultfunctionExample(){//useState()方法里面唯一的参数就是初始state,可以是数字或字符串或对象//这一步设置了count的初始值为0//setCount为修改count这个状态的方法//调用一次setCount,React会重新渲染Example组件const[count,setCount]=useState(0)return(<div>youcount:{count}{/*每次点击按钮调用setCount,会让count+1*/}<buttononClick={()=>setCount(count+1)}>Add</button></div>)}自定义useState某些时候我们需要自定义Hook,这可以将组件逻辑提取到可重用的函数中//自定义的Hook需要以大写字母开头//这里的Hook只控制count+1,因此可以复用//我们只需多次使用,就如下面的count1和count2,他们是互相独立的functionUseState(initvalValue){const[count,setCount]=useState(initvalValue)return[count,()=>{setCount(count+1)}]}exportdefaultfunctionExample(){const[count1,addCount1]=UseState(0)const[count2,addCount2]=UseState(0)return(<div>youcount1:{count1}{/*每次点击按钮调用setCount,会让count+1*/}<buttononClick={addCount1}>Add1</button>youcount2:{count2}{/*每次点击按钮调用setCount,会让count+1*/}<buttononClick={addCount2}>Add2</button></div>)}2.useEffectEffectHook可以让你在函数组件中执行副作用操作,类似于componentDidMount和componentDidUpdateexportdefault()=>{const[count,setCount]=useState(0);//在渲染后将执行该操作,并且在执行DOM更新之后也会调用它//effect可以返回一个清除函数,如此可以清除上一步的操作//可以接受两个参数,第一个就是操作本身,第二个是更新的依赖//可以选择不传,不传将依赖所有state变化//如果传入空,或者传一个确定的数如[1],那么该函数只会执行一次,即第一次渲染//可以传[count],则表示资源count变化才会调用useEffect(()=>{document.title=`Youclicked${count}times`;},[count]);//当然可以定义多个effect函数,它们会各自执行const[person,setPerson]=useState(1);useEffect(()=>{//},[person]);return(<div><p>Youclicked{count}times</p><buttononClick={()=>setCount(count+1)}>Clickme</button></div>);}自定义useEffect某些时候我们需要自定义Hook,这可以将组件逻辑提取到可重用的函数中//这里实现一个count自动+1的函数//自定义的Hook需要以大写字母开头//可以不传useEffect的第二个参数,这和传入callback,time一样//如果第二个传入为空将会出现神奇的事情://会发现console会一直打印0,但是count却一直都是1//这是因为未传依赖导致每次更新都在运行第一个延时函数,如果需要变化count可以有两种方法//第一种是,传第二个参数为[callback,time]或者不传第二个参数//第二种是,将Example里的setCount(count+1)改为setCount(count=>count+1)//这种方法将会改变作用域,每次都会取最新的count,大多数情况下都推荐以函数的形式functionUserInterval(callback,time){useEffect(()=>{consti=setInterval(callback,time)return()=>{clearInterval(i)}},[callback,time])}exportdefaultfunctionExample(){const[count,setCount]=useState(0)//第一种:传第二个参数为[callback,time]或者不传第二个参数UserInterval(()=>{console.log(count)setCount(count=>count+1)},1000)//第二种:将Example里的setCount(count+1)改为setCount(count=>count+1)//UserInterval(()=>{//console.log(count)//setCount(count=>count+1)//},1000)return(<div>youcount:{count}</div>)}3.useContext接收一个context对象(React.createContext的返回值)并返回该context的当前值适用于多个函数需要使用一个值,但是不推荐直接将该值引进函数中,这会造成函数本身冗杂因此需要通过父传子的形式一层层传递,但是这是非常麻烦的,我们可以使用useContext来直接访问//以白黑切换来讲解//这里创建一个白黑模式样式constthemes={light:{background:'#fff'},dark:{background:'#000'}}//创建一个context对象,包含白黑切换方法以及当前颜色constThemeContext=React.createContext({theme:themes.light,toggle:()=>{}})exportdefaultfunctionExample(){const[theme,setTheme]=useState(themes.light)//需要读取context的值以及订阅context的变化//仍然需要在上层组件树中使用<MyContext.Provider>来为下层组件提供context。//MyContext为自定义的,本文为ThemeContextreturn<ThemeContext.Providervalue={{theme,toggle:()=>{setTheme(theme===themes.light?themes.dark:themes.light)}}}><Toolbar/></ThemeContext.Provider>}constToolbar=()=>{return<ThemeBitton/>}//如果需要拿到Example组件里的theme,我们需要传递三层,十分麻烦//因此可以使用useContext来连接ThemeContext,直接操作themeconstThemeBitton=()=>{//useContext接收一个context对象(React.createContext的返回值)//并返回该context的当前值constcontext=useContext(ThemeContext)return<buttonstyle={{background:context.theme.background}}onClick={context.toggle}>ClickMe</button>}4.useReducer它接收一个形如(state,action)=>newState的reducer,并返回当前的state以及与其配套的dispatch方法如果你熟悉Redux的话,就已经知道它如何工作了//创建一个reducer,存储satatefunctionreducer(state,action){switch(action.type){case'add':returnstate+1case'sub':returnstate-1default:returnstate}}exportdefault()=>{//有两种不同初始化useReducerstate的方式,你可以根据使用场景选择其中的一种//第一个参数即为自定义的reducer//第一种,将初始state作为第二个参数传入useReducer是最简单的方法//counter即被设置为reducer返回的state//注意:state可以为对象,那么传入初始值也需为对象const[counter,dispatch]=useReducer(reducer,0)return(<div>count:{counter}{/*通过dispatch来触发reducer里的action来改变state的值参数即为action*/}<buttononClick={()=>dispatch({type:'add'})}>+</button><buttononClick={()=>dispatch({type:'sub'})}>-</button></div>)}//functioninit(initialCount){////dosomething//returninitialCount;//}//exportdefaultfunctionExample(initialCount){////第二种,需要将init函数作为useReducer的第三个参数传入,这样初始state将被设置为init(initialArg)////这么做可以将用于计算state的逻辑提取到reducer外部,这也为将来对重置state的action做处理提供了便利//const[counter,dispatch]=useReducer(reducer,initialCount,init);//return(//<div>//count:{counter}//{/*通过dispatch来触发reducer里的action来改变state的值//参数即为action*/}//<buttononClick={()=>dispatch({type:'add'})}>+</button>//<buttononClick={()=>dispatch({type:'sub'})}>-</button>//</div>//)//}5.useContext+useReducer实现类似reduximport{useReducer,createContext,useContext,Component}from'react';constinitState={count:0,list:[],};constreducer=(state,action)=>{constnewState=JSON.parse(JSON.stringify(state));switch(action.type){case'Add':if(action.data){newState.count+=action.data;}else{newState.count++;}returnnewState;case'Append':newState.list.push(action.data);returnnewState;default:returnnewState;}};//创建contextconstContext=createContext();constContextProvider=({children})=>{//创建reducerconst[state,dispatch]=useReducer(reducer,initState);return(<Context.Providervalue={{state,dispatch}}>{children}</Context.Provider>);};constApp=()=>{const{state,dispatch}=useContext(Context);const{count,list}=state;constadd=(v)=>{dispatch({type:'Add',data:v});};return(<div><span>{count}</span><buttononClick={()=>add()}>Add</button><buttononClick={()=>add(20)}>Add20</button><ul>{list.map((v)=>(<likey={v}>{v}</li>))}<ponClick={()=>dispatch({type:'Append',data:Date.now()})}>添加</p></ul><ClassComponent/></div>);};classClassComponentextendsComponent{state={};componentWillMount(){console.log('ClassComponentcomponentWillMount');}componentDidMount(){console.log('ClassComponentcomponentDidMount');}add=()=>{this.context.dispatch({type:'Add'});};render(){const{state,dispatch}=this.context;const{count,list}=state;return(<div><span>{count}</span><buttononClick={()=>this.add()}>Add</button><ul>{list.map((v)=>(<likey={v}>{v}</li>))}<ponClick={()=>dispatch({type:'Append',data:Date.now()})}>添加</p></ul></div>);}}ClassComponent.contextType=Context;exportdefaultfunction(){return(<ContextProvider><App/></ContextProvider>);}6.useRefuseRef返回一个可变的ref对象,其.current属性被初始化为传入的参数(initialValue)返回的ref对象在组件的整个生命周期内保持不变//点击按钮focus输入框exportdefault()=>{constrefInput=useRef()return(<div><inputref={refInput}/><buttononClick={()=>{//需要使用.current拿到dom元素refInput.current.focus()}}>Focus</button></div>)}7.一些额外的Hook还有一些额外的Hook,这里不详细介绍useCallback把内联回调函数及依赖项数组作为参数传入useCallback,它将返回该回调函数的memoized版本,该回调函数仅在某个依赖项改变时才会更新useMemo把“创建”函数和依赖项数组作为参数传入useMemo,它仅会在某个依赖项改变时才重新计算memoized值。这种优化有助于避免在每次渲染时都进行高开销的计算useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用ref这样的命令式代码useLayoutEffect其函数签名与useEffect相同,但它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并同步触发重渲染useDebugValueuseDebugValue可用于在React开发者工具中显示自定义hook的标签这些比较少用,各位可以前往React官网的Hook栏学习
暂无评论,我会出手
说点什么
登录用户可以修改和删除评论,可以收到回复的邮件提醒点击登录/注册
最多上传8张图片,仅支持jpg,png格式图片,单张大小5MB以内!
用户名: