在react类组件(class)写法中,有setState和生命周期对状态进行管理,但是在函数组件中不存在,因此引入hooks
React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。
使用hooks理由
高阶组件为了复用,导致代码层级复杂
生命周期的复杂
写成functional组件,无状态组件 ,因为需要状态,又改成了class,成本高
类组件与函数组件的区别:
函数组件没有生命周期,类组件有(挂载、更新、销毁)
函数组件没有状态(state),类组件有
函数组件没有this,类组件有
纯函数组件没有状态,useState()
用于设置和使用组件的状态属性
const [state, setstate] = useState(initialState)
//state:初始状态属性,指向状态当前值
//setstate:修改状态属性函数,用来更新状态
//initialState:状态的初始值,该值会赋给state
state是一个对象:
setState()不会局部更新
useEffect()
是副作用的钩子,可以检测数据更新 ,可以实现特定的功能,如异步请求
useEffect(() => {
//effect
return () => {
//cleanup
};
}, [依赖的状态;空数组,表示不依赖])
不要对 Dependencies 撒谎,如果你明明使用了某个变量,却没有申明在依赖中,你等于向 React撒了谎,后果就是,当依赖的变量改变时,useEffffect也不会再次执行, eslint会报警告
useEffffect和useLayoutEffffect区别:
简单来说就是调用时机不useLayoutEffect() 和原来componentDidMount &componentDidUpdate
一致,在react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。而useEffect()
是会在整个页面渲染完才会异步调用。
在实际使用时如果想避免页面抖动(在 useEffect 里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放useLayoutEffect
里。在这里做点dom操作,这些dom修改会和 react 做出的更改一起被一次性渲染到屏幕上
防止因为组件重新渲染,导致方法被重新创建,起到缓存作用;只有第二个参数 变化了,才重新声明一次
var handleClick = useCallback(()=>{
console.log(name)
},[name])
<button onClick={()=>handleClick()}>hello</button>
//只有name改变后, 这个函数才会重新声明一次,
//如果传入空数组, 那么就是第一次创建后就被缓存, 如果name后期改变了,拿到的还是老的name。
//如果不传第二个参数,每次都会重新声明一次,拿到的就是最新的name.
useCallback() 的功能完全可以由 useMemo()
所取代,使用 useMemo() 也可以返回一个记忆函数
useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs).
useCallback()与useMemo()
的区别:
用于在函数组件中获取真实的DOM元素对象或者是组件实例。(因为函数组件没有实例,所以这里的获取组件实例指的是获取类组件实例)
返回值是一个可变的ref对象,并且这个对象的值发生改变时不会引起页面的渲染。
const myswiper = useRef(null);
<Swiper ref={myswipe}/
useRef()
可以存储不需要引起页面渲染的数据;修改useRef值的唯一方法是修改.current,且修改后不会引起重渲染。
在使用React的过程中,如遇到状态管理,一般会用到Redux,而React本身是不提供状态管理的。而useReducer()提供了状态管理
useState()
的替代方案,用于包含多种状态,或者下一个 state 依赖于之前的 state,实现函数组件的状态管理。
基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。
useContext()可以共享状态,作用是进行状态的分发,避免了使用Props进行数据的传递
import React from 'react'
var GlobalContext= React.createContext()
// 注意此时的reduecer 返回值是一个对象 {isShow:false,list:[]}
function App(props){
let [state,dispatch] = useReducer(reducer,{isShow:true,list:[]})
return <GlobalContext.Provider value={{
dispatch
}}>
<div>
{
state.isShow?
<div >我是选项卡</div>
:null
}
{props.children}
</div>
</GlobalContext.Provider>
}
function Detail(){
var {dispatch} = useContext(GlobalContext)
useEffect(() => {
//隐藏
dispatch({
type:"Hide",
payload:false
})
return () => {
//显示
dispatch({
type:"Show",
payload:true
})
};
}, [])
return <div>
detail
</div>
}
当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。
用户自定义的Hooks
:
命名的要求:用use开头,后跟名称(首字母大写)
作用:根据具体业务的需求,对Hooks中默认的钩子函数进行封装,使代码的结构更加清晰,便于使用和维护
import React, { useEffect, useState } from 'react'
function useToLocaleUpperCase(text) {
const [name, setName] = useState(text)
useEffect(() => {
setName(name.toLocaleUpperCase())
}, [])
return { name };
}
export default function App() {
const { name } = useToLocaleUpperCase('zhansan')
return (
<div>App-{name}
<Child></Child>
</div>
)
}
function Child() {
const { name } = useToLocaleUpperCase('lisi')
return (
<div>App-{name}</div>
)
}