昨天没有梳理完的今天接着梳理
- userContext()
- userReducer()
- useMemo()
- useCallback()
4. userContext
- userContext的作用
userContext就是context的接收方式不同,在前边的react传值梳理中我们就已经说过,有一种方式就是context传值,context在react中就如字面含义一样,是我们react使用中的一种容器,通过React.createContext来创建一个容器ContextObj
,容器作为标签ContextObj.Provider
包裹我们要传递值的生产者,那我们内部的所有消费者,也就是子组件,都可以通过ContextObj.Consumer
来包裹,然后通过箭头函数的模式获取到参数value,内部也都可以正常使用整个value的值,但是无疑这种方法是繁琐的,那么我们函数式组件中的跨级传输救星来了,就是userContext
,userContext
可以直接获取到我们所需的值,让消费者解放出来,不再需要标签包裹就能获取到相应的参数 - userContext使用
import React,{ useContext } from 'react'
const Ceshi = () => {
const AppContext = React.createContext({})
const A =() => {
const { name } = useContext(AppContext)
return (
<p>我是A组件的名字{name}<span>我是A的子组件{name}</span></p>
)
}
const B =() => {
const { name } = useContext(AppContext)
return (
<p>我是B组件的名字{name}</p>
)
}
return (
<AppContext.Provider value={{name: 'hook测试'}}>
<A/>
<B/>
</AppContext.Provider>
)
}
export default Ceshi
如果有不太了解的可以看我前边梳理的react传值,那里有说明如何使用context传值方式,有了userContext也只是让我们获取传值获取的更加便捷,代码更加的简洁,但是userContext也是有缺点的,或者说context传值方式也是有缺点的,就是会使代码的复用性变得更差,为什么这样说,因为我们就算不用标签包裹的方式之后,还是需要组件和父组件的传值进行逐个对应的,不然我们的子组件被抽离以后需要的传值我们就无法获取了,就算我们给子组件的获取传值做了兼容,也会固定化父组件的传值方式
5. userReducer
-
userReducer的作用
我们之前梳理的rudex我们可以看一下,我们也可以在这里简单说一下,redux是react的一种状态共享方式,内部有一个很重要的概念就是reducer,reducer就是作为我们action,也就是通知处理方案的函数,这个函数会接收到两个值,一个是state,也就是旧时的状态,另一个就是我们的action通知,且可以拿到我们的dispatch通知时候传递过来的type和传递的value,这两个值一个可以让我们做出判断走哪里的修改逻辑,另一个可以让我们获取到要修改替换的属性的新值,从而达到对旧值更改的目的,当然了我们的userReducer也是从rudex中吸取的经验所产生的,只不过userReducer是直接可以在hooks中进行使用的,不用引入新的rudex和rudex-react,内置hooks -
userReducer的使用
import React,{useReducer} from 'react'
const AddCount = () => {
const reducer = (state, action) => {
if(action.type === 'add'){
return {
...state,
count: state.count +1,
}
}else {
return state
}
}
const addcount = () => {
dispatch({
type: 'add'
})
}
const [state, dispatch] = useReducer(reducer, {count: 0})
return (
<>
<p>{state.count}</p>
<button onClick={addcount}>count++</button>
</>
)
}
export default AddCount
从上边的代码我们可以看到,我们把整个的代码逻辑抽离了出来,不管是状态还是改变状态的函数,都已经被我们改变,我们没有再用到useState去生成变量且在dom逻辑中使用,而是用了外部生成变量且外部dispatch通知修改变量这样的方式,其实从这里我们看不到优势,而且我们的useReducer每次调用都会产生不同的state和dispatch,这样我们在子组件中使用的时候,就无法和redux一样进行状态的集中管理了,这就要看我们上边的useContext了,这也是为什么我在前一个章节说他们是一对儿的原因
- 与useContext配合使用
import React from 'react';
import './style.css';
//createContext函数内可以传递一个默认的共享数据,我们用不到所以传
const ContextObj = React.createContext();
function AAA() {
const { dispatch} = useContext(AppContext)
return (
<div>
我是组件1我除了自己的逻辑,我还可以更改组件2获取的值
<button
onClick={() => {
dispatch({
type: 'change',
name: 333
})
}}
>
点击改变值
</button>
</div>
);
}
function BBB() {
const { name } = useContext(AppContext)
return (
<div>我是组件2获取到的值:{name}</div>
);
}
function App() {
const AppContext = React.createContext({})
const reducer = (state, action) => {
if(action.type === 'change'){
return {
...state,
name: action.name,
}
}else {
return state
}
}
const [state, dispatch] = useReducer(reducer, {name: 222})
return (
<ContextObj.Provider
value={{name, dispatch}}
>
<div>
<AAA />
<BBB />
</div>
</ContextObj.Provider>
);
}
export default App;
这个是我拿我之前做的context传值方式中的例子进行改造的一个,如果想要了解清楚,建议可以对比着看传值梳理,我们从例子中可以看到,我们context做了什么,把state和dispacth进行了传值共享,这样的话我们的每个子组件都可以通过useContext来获取到state和dispatch,这样我们在任何的子组件中都可以访问到我们父组件中useReducer创建的状态和状态管理,也就达到了我们一系列组件和子组件公用一套状态的目的,要记住
React.createContext
是必不可少的东西,虽然我们用useContext接收但是我们传递还是用react原本的方法去传递
6. useMemo
我们都知道react是自动进行更新且会自动更新所有的子组件的,我们的useMemo需要我们从头说起,从最开始的缓存说起
PureComponent
我们之前没有说过这个,这次趁机说一下这个PureComponent,这个的用法就是在我们进行类组件的创建的时候,不再从react.Component来继承,而是从PureComponent来继承,如此一来我们的类组件会自动多一个方法,就是每次更新的时候把props和state做一次浅比较,看一看我们使用的组件需不需要做出更新,当然了我们之前也说过,用shouldComponentUpdate可以自定义比较方法,根据我们自定义的比较返回true或者false来表明是否需要更新render,但是如果我们用PureComponent就可以自己进行浅比较
import React, { PureComponent } from 'react';
class IndexPage extends PureComponent{
constructor() {
super();
this.state = {
isShow: 1
};
}
render() {
return (
<div>
<div>{this.state.isShow}</div>
</div>
);
}
}
React.memo
react的HOC(高阶组件),我们可以通过这个高阶组件进行缓存的处理,每次我们的父组件更新的时候,用这个高阶组件包装导出的组件都会进行对比方法的调用,当然了对比方法就是我们在进行高阶组件包装时候传入的方法,且注意,react.memo就是为了我们的函数式组件的缓存出现的,所以我们函数式组件很适合用这个高阶组件
import React, { memo } from 'react';
function MyComponent(props) {
return <div>{props.name}<div>
}
function areEqual(prevProps, nextProps) {
//做旧props和新props的对比
}
export default memo(MyComponent, areEqual);
useMemo
useMemo为我们提供的是更加简便的用法以及更加只能的监控数据是否改变,我们可以把我们依赖的值细分且传入useMemo包裹之后的组件中的第二个数组参数中,这样useMemo就会替我们做对比,来判断是否要更新组件,依赖的传入方式使我们开发更加的顺畅,不用每次都写那么多的比较逻辑,重复且复杂
import React, { useMemo } from 'react';
function MyComponent(props) {
return useMemo(() => {
return <div>
<p>number is : {props.number}</p>
</div>
}, [props.number]);
}
export default MyComponent;
7. useCallback
- useCallback的作用
useCallback与上边讲的几个缓存的方式不同,useCallback做的是函数的缓存,为什么要做函数的缓存,要知道我们在整个函数组件中,不管任何东西做出改变,整个的函数一定意义上是要重新走一遍的,也就意味着我们的函数每次都是进行了一次重新定义,js中的对象,进行两次完全一样的创建,进行全等对比,都是不相等的,为什么,因为对象是引用变量,那么我们就知道函数也是引用变量,每次传递给子组件的函数都是作用一样,但是引用是不一样的,这就会导致我们传递的参数实际上是在每次父组件更新时都在发生改变的,我们就可以对函数进行缓存,这样每次组件刷新生成的函数都是同一个引用,传递的时候子组件接收到的也就是同一个函数了
import React, { memo, useCallback, useState } from 'react'
function PageA (props) {
const { onClick, children } = props
return <div onClick={onClick}>{children}</div>
}
function PageB ({ onClick, name }) {
return <div onClick={onClick}>{name}</div>
}
const PageC = memo(PageB)
function UseCallback() {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const handleClick1 = () => {
setA(a + 1)
}
const handleClick2 = useCallback(() => {
setB(b + 1)
}, [b])
return (
<>
<PageA onClick={handleClick1}>{a}</PageA>
<PageC onClick={handleClick2} name={b} />
</>
)
}
export default UseCallback
从以上代码我们可以看出,我们使用useCallback的时候跟memo进行搭配使用,因为memo不传递第二个函数的时候,进行比较就是选择性的浅比较,那么我们的状态浅比较并没有更改的情况下,我们传递的方法如果也是完全相同的,那么我们的子组件就不会进行更新了,反之我们如果没有用UseCallback,那我们的传递的方法每次都在进行改变,其实还是进行更新的,所以,这也是一对儿
总结: 其实react的函数式组件中的hooks都是不太复杂的,如果不了解原理只说使用的话,最简单的例子就可以说明清楚,我们加上我们自己的文字陈述之后,能更加容易的理解且记住我们hooks的具体用法,一切皆有定式,所以我们拿来就用,其实也没错啦