React Hooks
就是用函数的形式代替原来的继承类的形式,并且使用预函数的形式管理state
,有Hooks可以不再使用类的形式定义组件了。这时候你的认知也要发生变化了,原来把组件分为有状态组件和无状态组件,有状态组件用类的形式声明,无状态组件用函数的形式声明。那现在所有的组件都可以用函数来声明了。
用了它简直不要太爽,可以说是改变了编程的方式。
下面对比下就知道多爽:
class的方式:
import React, { Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = { count:0 }
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.addCount.bind(this)}>Chlick me</button>
</div>
);
}
addCount(){
this.setState({count:this.state.count+1})
}
}
export default Example;
hook写法:
import React, { useState } from 'react';
function Example(){
const [ count , setCount ] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example;
直观上代码变少了,而且易理解,符合我们函数式编程的思想。
hooks的目的就是让你不再写class
,让function
一统江湖。
const [ count , setCount ] = useState(0);
分析下,es6语法里用数组结构来定义变量和一个设置变量的方法,后面的useState相当于给变量附一个初值。
<button onClick={()=>{setCount(count+1)}}>click me</button>
利用setCount方法直接写一个箭头函数,且react自带记忆功能,能帮我们记忆上一次的状态值。
那变量多个的时候,usestate怎么自己找到变量赋初值呢?其实也不难,就是按顺序一个一个赋值。且useState不能放在条件语句里,必须按相同的顺序来渲染。
用useEffect代替常用的生命周期函数,再来个对比吧,看看多方便:
这次先来看hook方式:
import React, { useState , useEffect } from 'react';
function Example(){
const [ count , setCount ] = useState(0);
//---关键代码---------start-------
useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
})
//---关键代码---------end-------
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example;
再看看原来class的方式:
import React, { Component } from ‘react’;
class Example3 extends Component {
constructor(props) {
super(props);
this.state = { count:0 }
}
componentDidMount(){
console.log(`ComponentDidMount=>You clicked ${this.state.count} times`)
}
componentDidUpdate(){
console.log(`componentDidUpdate=>You clicked ${this.state.count} times`)
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.addCount.bind(this)}>Chlick me</button>
</div>
);
}
addCount(){
this.setState({count:this.state.count+1})
}
}
export default Example3;
效果一样,这是为何?因为useEffect在组件第一次渲染和以后的每次更新都会运行,即包含上面两个生命周期函数的功能,用return方式可实现componentwillunmount(组件解绑)。像这样:
function Index() {
useEffect(()=>{
console.log('useEffect=>老弟你来了!Index页面')
return ()=>{
console.log('老弟,你走了!Index页面')
}
})
return <h2>JSPang.com</h2>;
}
return的第二个参数意思是:当里面变量的值改变时,才启动return的功能,即解绑功能。如下:
function Example(){
const [ count , setCount ] = useState(0);
useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
return ()=>{
console.log('====================')
}
},[count]) //关键代码
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
<Router>
<ul>
<li> <Link to="/">首页</Link> </li>
<li><Link to="/list/">列表</Link> </li>
</ul>
<Route path="/" exact component={Index} />
<Route path="/list/" component={List} />
</Router>
</div>
)
}
useContext让父子传值更简单,对它所包含的组件树提供全局共享数据的一种技术。需要注意的是useContext
和redux
的作用域是不同的,一个解决的是组件之间值传递的问题,一个是应用中统一管理状态的问题,但通过和useReducer
的配合使用,可以实现类似Redux
的作用。
用法也很简单,先为对应的变量创造上下文,在标签里调用:
import React, { useState , createContext } from ‘react’;
//===关键代码
const CountContext = createContext()
function Example4(){
const [ count , setCount ] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
{/*======关键代码 */}
<CountContext.Provider value={count}>
<Counter /> //子组件
</CountContext.Provider>
</div>
)
}
export default Example4;
子组件用use调用即可:
function Counter(){
const count = useContext(CountContext) //一句话就可以得到count
return (<h2>{count}</h2>)
}
useReducer:也是reacthook自带的函数,让代码有更好的可读性和可维护性,给测试提供方便,下面给个最简单的例子:一个参数是状态,一个是控制状态的参数。
function countReducer(state, action) {
switch(action.type) {
case ‘add’:
return state + 1;
case ‘sub’:
return state - 1;
default:
return state;
}
}
- 从理论层面讨论usecontext和usereducer代替redux的可能:
useContext
:可访问全局状态,避免一层层的传递状态。这符合Redux
其中的一项规则,就是状态全局化,并能统一管理。useReducer
:通过action的传递,更新复杂逻辑的状态,主要是可以实现类似Redux
中的Reducer
部分,实现业务逻辑的可行性。
useMomo优化性能:因为失去shouldcomponentupdate这个生命周期函数,前文提到这个函数可用于性能优化,当返回true时,才更新组件。而且在函数组件里不再区分挂载(mount)和更新(update)两个状态,所以每次都是执行内部的全部逻辑,这样会导致极大的性能浪费。
useMemo(()=>changeXiaohong(name),[name])
只有当第二个参数匹配成功,才会执行。
useref:有两个作用1.获取jsx里的dom元素;2.可用来保存变量,其实用usecontext完全可以代替。
自定义hook函数和创建组件很相似,前者偏向于功能,后者偏向于界面和业务逻辑,以use开头:每次修改状态的方法onResize;useCallback(),目的是为了缓存方法,用useeffect来注册resize来监听时间。
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;
}