由Vue转React的自学文档
由 vue3 转学 react,之前从未接触过 react,自学了一遍感觉还好,容易上手。
期间的一些笔记,后续有更深的见解持续更新。
组件(类组件,函数组件)
- 组件名称首字母要大写,必须有返回值,没有写null
- 设置默认值:function 组件名({默认值:1})/ 类组件:static defaultProps ={ 默认值:1 }
- 实例化:类组件有实例化,函数组件没有
组件通信
父传子
- 父组件通过 state 传递值。state 里设置要传递的值
- 给子组件标签添加属性,msg={this.state.msg1}
- 子组件通过 props 接受父组件传来的数据(类组件用
this.props
/函数组件通过参数)获取 ->props
对象
子传父
子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数实参传入即可
兄弟组件通信
利用共同的父组件,实现子传父,父在传子的兄弟通信
跨组件(祖孙)通信 Context
在嵌套组件树里,采用 Context 不需要通过层层 props 传递,就能进行数据传递。
- 创建 Context 对象,导出 Provider 和 Consumer 对象
- 使用 Provider 包含根组件提供数据
- 将需要用到组件,使用 Consumer 包裹,然后 value => 获取
1 | //祖组件 |
child 属性:在组件内部使用都会自带这一属性,可以是文本,标签元素,函数,jsx
1 | //例如父组件 |
生命周期
挂载阶段:
constructor(初始化 state,创建 Ref,使用 bind 解决 this 问题) ,初始化最先执行,只执行一次。
render (渲染 UI),每次组件渲染的时候都会触发。
componentDidMount(发送网络请求,Dom 操作),完成渲染后执行,初始化的时候执行一次。
更新阶段:
- render(与挂载相同,是同一个 render),每次组件都会触发
- componentDidUpdate(Dom 操作,可以获取到更新后的 Dom,不要直接调用 setState),更新后渲染
卸载阶段
- componentWillUnmount(执行清理工作,比如清理定时器),组件卸载
hooks 的一些 API
本质:让函数组件更强大更灵活的钩子,只能在函数组件(有状态)中使用。
作用:组件状态复用,class 组件自身问题
注意:只能写在函数组件最外层,不能写在 if,for 循环判断里面(官文文档解释为 react 的运行机制要保持这些 hooks 的顺序的唯一性),可以将回调函数作为参数传递
useState
const [count, setCount] = useState(initCount);
useState 传入的是当前 count 初始值也就是 initCount(可以是初始值也可以是函数),然后返回的是最新的 count,和一个修改 count 的方法(可以普通调用和函数调用,,数据是对象类型的话,一般要结合拓展运算符复制一份)。
1 | //三种情况 |
初始值 initCount 只有在首次渲染生效,后续更新会被忽略,直接调用 setCount
一句话:在同步里,异步更新状态和 dom,在异步里,同步更新状态和 dom。
useEffect:为更好处理副作用(除了更新 UI 外的作用,如 ajax,本地存储)
感觉像 vue 的 nextTick。
React 会在每次渲染完后调用 useEffect,包括第一次加载渲染 DOM。
useEffect 设计初衷是用来取代 componentDidMount 和 componentDidUpdate,它接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
加 [ ] 为行业只执行义一次的默认写法,一般用于发送请求:
1 | useEffect(() => { |
而 useLayoutEffect 的作用和 useEffect 几乎差不多,几乎看不到任何差别,但它们的渲染底层逻辑就稍微不同。
具体可见这篇文章,用一个例子很清楚讲明,在性能优化上尽量选用 useEffect,在解决页面渲染更新会闪烁可以用 useLayoutEffect。
useRef:获取实例 dom 或组件方法,必须是类组件
步骤:导入 useRef 函数,通过 ref 绑定要获取的元素,执行 useRef 并传入 null,这将会返回一个含有current属性的对象。
1 | import React, { useRef, useEffect } from 'react' |
useContext:祖孙组件响应式更新
一个例子直接说明梗概。
1 | import React, { createContext, useContext, useState } from 'react' |
useReducer
const [ state,dispatch ] = useReducer(reducer, initState, init)
接收三个参数,分别为:处理状态更新的 reducer,状态初始值,状态初始化函数。
有两种传递方式:不传第三个参数,如 1;state 数据比较复杂,可以将第二个参数作为第三个参数传入,如 2。
最后,useReducer 将返回两个东西,一个当前最新的状态 state,一个是更新状态的 dispatch。
1 | //1.将状态初始值作为第二个参数传入 |
useCallback
入参和 useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
那为啥要用 useCallback:那是因为在函数组件里的函数,会随着状态值的更新而重新渲染,函数也会频繁被定义且组件通信很耗性能。使用useCallback+memo可以解决上述问题
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。
useMemo(相当于 vue 的 computed)
入参和 useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。如果没有提供依赖项数组,useMemo
在每次渲染时都会计算新的值。
官方建议:不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect
的适用范畴,而不是 useMemo
。
useCallback + useMemo
痛点见上述
1 | //使用Memo后确实不会影响了,但父组件传值过来呢 |
路由:react-router-dom
先下载react-router-dom@6依赖,在导入import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
, 基本使用如下。BrowserRouter:包裹整个路由,一个 react 应用只需使用一次,还有个路由模式:hashRouter
1 | <BrowserRouter> |
编程式导航:replace 设置为 true 表示不保留历史记录
1 | import { useNavigate } from 'react-router' |
编程式导航传参方式:searchParams / params
1 | //searchParams 传参:和取参 |
二级路由:如果要设置一级路由默认渲染的二级路由,可如下例所示
1 | //给URL地址about后面添加nextRouter二级路由 |
mobx
校验规则第三方插件: prop-type
标签传递 Boolean 值需要按对象形式传递,如 isTrue= {false},而非 isTrue= “false”
可以传递任何数据,包括函数,jsx
给组件添加内置属性或方法
1 | //添加属性验证 |
添加校验规则:需要另外安装第三方插件npm install prop-type
,导入prop-types
包,在组件名.propTypes={}
添加校验规则。
可检验的规则有:
- 常见类型 array、bool、fun、number、obj、string
- 基本类型:element
- 必填项:isRequired
- 特定结构对象:shape({})
1 | import Proptypes from 'prop-types' |
其他
获取输入框 value 值的几种方法
- 通过在
state
中定义变量,在输入框绑定value={this.state.component} ,和 onChange={this.changeInputValue} 事件
,通过事件修改 state 的中变量。
1 | changeInputValue = (e) => { |
- 通过 ref 拿到。先声明
myRef = React.createRef()
,在 input 标签内绑定ref={this.myRef}
,最后通过this.myRef.current.value
拿到 value 值。
修改数组的某项状态
删除:直接 filter 过滤传过来的值不等于数组的值就好,或者根据索引 splice(index, 1)
item 项
1 | this.setState({ list: this.state.list.filter((item) => item.id !== id) }) |
修改:一般对整个数组 map 遍历到需要修改的项
1 | list: this.state.list.map((item) => { |