对函数型组件进行增强,让函数型组件可以存储状态,一个拥有处理副作用的能力。
组件中只要不是把数据转换成视图的代码,就是属于副作用
例如:获取dom元素,为dom元素添加事件,定时器,发送AJAX请求等 都是属于副作用代码。在函数型组件当中我们要使用Hooks 来处理这些个副作用。
对函数型组件进行增强,让开发者在不使用类组件的情况下,实现相同的功能。
缺少逻辑复用机制
✌️为了复用逻辑增加无实际渲染效果的组件,增加了组件层级 显示十分臃肿
✌️增加了调试的难度以及运行效率的降低
类组件经常会变的很复杂 难以维护
✌️将一组相干的业务逻辑炒粉到了多个生命周期函数中
✌️在一个生命周期函数内存在多个不相干的业务逻辑
类成员方法不能保证this 指向的正确性
Hooks 意为钩子,React Hooks 就是一堆钩子,React通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能,
userState()作用:用于函数组引入状态。
✌️在我们过往的使用中一个函数的变量在这个函数使用后就会被释放掉了。
✌️所以呢函数型组件在以往的的使用中是不可以保存状态的。
✌️而有了`useState()` 就可以让函数型组件保存状态。
// 计数器
import React,{useState} from 'react';
// 当我们点击之后组件会重新渲染,通过useState 保存状态
function App(){
const [count,setCount]=useState(0);
return <div>
<span>{count}</span>
<button onClick={()=>setCount(count+1)}>+1</button>
</div>;
}
import React,{useState} from 'react';
// 当我们点击之后组件会重新渲染,通过useState 保存状态
function App(props){
// 4 设置默认值,写在setState 里面是只有在初始渲染仕调用如果写在外层会每次重新渲染App组件都会调用。
const [count,setCount]=useState(()=>{
return props.count || 0
});
const [person,serPerson]=useState({name:'dudu',age:'18'})
return <div>
<span>{count}{person.name}{person.age}</span>
<button onClick={()=>setCount(count+1)}>+1</button>
<button onClick={()=>setPerson({...prerson,name:'嘟嘟'}}>+1</button>
</div>;
}
function App(props){
const [count,setCount]=useState(()=>{
return props.count || 0
});
const [person,serPerson]=useState({name:'dudu',age:'18'})
function handleCount(){
// 1.返回什么就是什么
setCount((count)=>{
let newCount=count + 1
return count + 1
})
//2 异步的
//document.title=count;
document.title=newCount;
}
return <div>
<span>{count}{person.name}{person.age}</span>
<button onClick={handleCount}>+1</button>
<button onClick={()=>setPerson({...prerson,name:'嘟嘟'}}>+1</button>
</div>;
}
useReducer()作用:是另一种让函数组件保存状态的方式。
import React,{useReducer} from 'react';
function App(){
// state:你在这存什么,这个state就是什么 ,要处理的东西
// action:组件触发按钮的时候,接受的行为 ,触发的aciton对象,匹配对象
function reducer (state,action){
switch (action.type){
case 'increament':
return state + 1;
case 'decrement':
return state -1;
default :
return state;
}
}
const [count,dispatch]=useReducer(reducer,0)
return <div>
<button onClick={()=>{dispatch({type:'decrement'})}}>-1</button>
<span>{count}</span>
<button onClick={()=>{dispatch({type:'increament'})}}>+1</button>
</div>
}
相对于useState() 好处:
比如说:App这个组件相对于某个子组件,想要去改变count 值,不需要传递多个方法,可以直接使用dispatch,通过触发action 直接改变count值
useContext() 作用:在跨组件层级获取数据时简化获取数据的代码。
import React,{createContext,useCOntext} from 'react'
const countContext = createContext();
function App(){
return <countContext.Provider value={100}>
<Foo />
</countContext.Provider>
}
function Foo(){
//return {value=>{
// return {value}
// }
// }
//简化代码
const count=useContext(countContext);
return <div>foo {count}</div>
}
userEffects()作用:让函数型组件拥有处理副作用的能力,类似生命周期函数
可以把useEffect 看作componentDidMount,componentDidUpdate ,componentWillUnmount这三个函数组合
useEfffect(()=>{}) | componentDidMount,componentDidUpdate
useEffect(()=>{},[]) | componentDidMount
useEffect(()=>() => {}) | componenWillUnmount
import React,{useState,useEffect} from 'react';
import ReactDom from 'react-dom'
function App(){
const [count,setCount]=useState(0);
// 组件挂载完成以后执行,组件数据更新之后执行
//useEffect(()=>{
// console.log('111'); /111`
//})
//组件挂载完成之后执行
//useEffect(()=>{
// console.log('111');
//},[])
// 组件卸载完成之前执行
useEffect(()=>{
return () => {
console.log('组件被卸载了')
}
})
return (
<div>
<span>{count}</span>
<botton onClick={()=>{setCount(count+1)}}></button>
<button onClick={()=>ReactDom.unmountComponentAtNode(dociment.getElementById('root'))}
</div>
)
}
import React,{useEffect} from 'react';
function App(){
function onScroll(){
console.log('页面滚动了')
}
//相当于componentDidMount挂载的时候执行一次
useEffect(()=>{
window.addEventListener('scroll',onScroll)
return ()=>{
window.removeEventListener('scroll' ,onScroll)
}
},[])
return <div> App. Works</div>
}
export default App
import React,{useEffect,useState} from 'react';
import ReactDOM from 'react-dom'
function App(){
function onScroll(){
console.log('页面滚动了')
}
//相当于componentDidMount挂载的时候执行一次
useEffect(()=>{
window.addEventListener('scroll',onScroll)
return ()=>{
window.removeEventListener('scroll' ,onScroll)
}
},[])
const [count,setCount]=useState[0]
//设置定时器要在组件挂载完成之后去做,清除定时器要在组件卸载之前,此案例中不需要组建更新数据之后执行,所以我们传递一个空数组,用于清理工作
useEffect(()=>{
const timerId=setInterval(()=>{
// setCount((count)=>count + 1)
setCount((count)=> {
document.title= count +1
return count + 1
})
},1000)
return ()=>{
//卸载之前 清除定时器
clearInterval(timerId)
}
},[])
//点击按钮的时候 卸载组建 指定在root 上面卸载组件
return <div> {count} <button onCLick={()=>ReactDOM.umountComponentAtNode(document.getElementById('root'))}>卸载组件</button></div>
}
export default App
useEffect() 处理副作用相比传统的类组件有什么优势?
按照用途将代码进行分类
将一组相干的业务逻辑归置到了同一个副作用函数中
通俗点可理解为:
由于useEffect() 方法可以被多次调用,
开发时可以按照用途把不同的代码放在不同的useEffect()中,
简化重复代码,是组件内部的代码更加清晰
类组件中 组件挂载完成和组件更新是两个不同的生命周期函数,
一般的业务逻辑和应用场景
组件挂载完成和组件更新之后要做的事情都是一样的。
import React,{useEffect,useState} from 'react'
function App(){
const [count,setCount]=useState(0);
//1. 只有挂载完成之后 与count 发生改变执行此钩子函数
useEffect(()=>{
document.title=count
},[count])
return <div>
<span>{count}</span>
<button onClick={()=>setCount(count +1 )}> +1</button>
</div>
}
useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回Promise。
异步方式获取返回值结果通过函数自执行的方法,
import React,{useEffect,useState} from 'react'
function App(){
const [count,setCount]=useState(0);
//1. 挂载完成之后 执行本effect
//useEffect(()=>{
//getData().then(result=>{
// console.log(result)
//})
//},[])
// 异步方式获取返回值结果通过函数自执行的方法,
useEffect(async()=>{
(async function(){
let result=await getData()
console.log(result)
})()
},[])
return <div>dudu</div>
}
// 模拟异步操作
function getData(){
return new Promise(resolve=>{
resolve(msg:'hello')
})
}
import {useMemo} from 'react';
const result =useMemo(()=>{
//如果count 值发生变化时函数重新执行
return result;
},[count])
案例证明:useMemo 会缓存计算结果,如果检测值没有发生变化,即使组件重新渲染,也不会重新计算。
import React,{useState,useMemo} from 'react';
function App(){
const [count,setCount]=useState(0)
const [bool.setBool]= useState(true)
const result=useMemo(()=>{
consel.log('1')
return count * 2
},[count])
retrun <div>
'count:' {count} 'result:'{result}
<span>{bool?'zhen':'jia'}</span>
<button onClick={()=>setCount(count + 1 )}>+1</button>
<button onClick={()=>setBool(!bool)}>+1</button>
</div>
}
性能优化,如果组件中的数据没有发生变化,阻止组件更新,类似类组件中PureComponent 和 shouldComponentUpdate
//回判断 Counter组件有没有变化,如果没有则组织更新
import React,{memo} from 'react'
function Counter(){
return <div></div>
}
export default memo(Counter)
验证案例
import React,{useState,memo} from 'react';
function App(){
const [count,setCount]=useState(0)
retrun <div>
'count:' {count}
<button onClick={()=>setCount(count + 1 )}>+1</button>
<Foo/>
}
//不需要重新渲染
const Foo= memo(function Foo(){
console.log('Foo')
return <div>我是Foo组件</div>
})
性能优化,缓存函数,是组件重新渲染事得到相同的函数实例
import React,{useCallback} from 'react';
function Counter (){
const [count,setCount]=useState(0);
//如果setCount 不发生变化。当前得到的这个函数 永远是一个函数实例
const resetCount=useCallback(()=>setCount(0),[setCount]);
return <div>
<span>{count}</span>
<button onClick={()=>setCount(count +1 )}>+1 </button>
</div>
}
使用案例:
在App组件中 创建一个方法,这个方法的作用是用来清除数值的,比如:当我们点击按钮 数值发生改变,当我们点击清除这个按钮的时候 让数值清零
import React,{useState,memo,useCallback} from 'react';
function App(){
const [count,setCount]=useState(0)
//这里使用 useCallback()
const resetCount=useCallback(()=>{
setCount(0)
//如果setCount 不发生变化。当前得到的这个函数 永远是一个函数实例
},[setCount])
retrun <div>
'count:' {count}
<button onClick={()=>setCount(count + 1 )}>+1</button>
//当点击+1时 App组件重新渲染。那么传给Foo组件的resetCount 函数也就会发生变化,对于Foo而言就是Foo数据发生变化,Foo组件就会重新渲染,解决问题方法:使用useCallback() 让App组件每次重新渲染饿时候 都能让我们得到一个相同的resetCount 函数 ,Foo就不会重新渲染
<Foo resetCount={resetCount}/>
}
//不需要重新渲染
const Foo= memo(function Foo(props){
console.log('Foo')
return <div>我是Foo组件
<button onClick=(props.resetCount)>reset</button>
</div>
})
如何使用useRef 获取dom对象
import React,{useRef} from 'react'
function App(){
const useName=useRef();
const handler=()=>console.log(useName)
return <input ref={useName} onChange={handler}/>
}
使用案例:
import React,{useRef} from 'react'
function App(){
const box=useRef();
return <div ref={box}>
<button onClick={()=>consolr.log(box)}>获取DIV</button>
</div>
}
即使组件重新渲染,保存的数据仍然还在,保存的数据被更改不会触发组件重新渲染
1.useState()保存的数据时状态数据,当数据发生改变的时候会触发组件重新渲染。而useRef()保存的数据不属于状态数据,更改数据也不回触发组件重新渲染。
2.我们通常使用useRef 去保存一些 程序运行过程中一些辅助作用的数据。
import React,{useRef,useState,useEffect} from 'react'
function App(){
const [count , setcount] =useSate(0)
let timerId=null
useEffect(()=>{
//当组件 更新时 timeId 要保存下来 不能丢,怎么办?
//使用current 属性timerId.current
timerId.current=setInterval(()=>{
setCount(count + 1)
},1000)
},[])
const stopCount=()=>{
clearInterval(timerId.current)
}
return <div>
{count}
<button onClcik=(()=>{stopCount})>停止</button>
</div>
}