由vue3转学react,之前从未接触过react,自学了一遍感觉还好,容易上手。
期间的一些笔记,后续有更深的见解持续更新。
父传子
this.props
/函数组件通过参数)获取 ->props
对象子传父
子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数实参传入即可
兄弟组件通信
利用共同的父组件,实现子传父,父在传子的兄弟通信
跨组件(祖孙)通信Context
在嵌套组件树里,采用Context不需要通过层层props传递,就能进行数据传递。
//祖组件
const { Provider,Consumer } = createContext()
<Provider value={this.state.nextMsg}>
<Brother/>
</Provider>
//孙组件
<Consumer>
{value => <span>{value}</span>}
</Consumer>
child属性:在组件内部使用都会自带这一属性,可以是文本,标签元素,函数,jsx
//例如父组件
<NextFather>
<div>
<p>标签</p>
{()=>{this.fun(id)}}
</div>
</NextFather>
//NextFather子组件
function NextFather({children}){
return(
<div>
{children}
</div>
)
}
挂载阶段:
constructor(初始化state,创建Ref,使用bind解决this问题) ,初始化最先执行,只执行一次。
render (渲染UI),每次组件渲染的时候都会触发。
componentDidMount(发送网络请求,Dom操作),完成渲染后执行,初始化的时候执行一次。
更新阶段:
卸载阶段
本质:让函数组件更强大更灵活的钩子,只能在函数组件(有状态)中使用。
作用:组件状态复用,class组件自身问题
注意:只能写在函数组件最外层,不能写在if,for循环判断里面(官文文档解释为react的运行机制要保持这些hooks的顺序的唯一性),可以将回调函数作为参数传递
const [count, setCount] = useState(initCount);
useState传入的是当前count初始值也就是initCount(可以是初始值也可以是函数),然后返回的是最新的count,和一个修改count的方法(可以普通调用和函数调用,数据是对象类型的话,一般要结合拓展运算符复制一份)。
//三种情况
setCount(count + 1)
setCount(() => count + 1)
setCount(() => {
return{
...count,
//再对象赋值
}
})
初始值initCount只有在首次渲染生效,后续更新会被忽略,直接调用setCount
一句话:在同步里,异步更新状态和dom,在异步里,同步更新状态和dom。
感觉像vue的nextTick。
React会在每次渲染完后调用useEffect,包括第一次加载渲染DOM。
useEffect 设计初衷是用来取代 componentDidMount 和 componentDidUpdate,它接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
加 [ ] 为行业只执行义一次的默认写法,一般用于发送请求:
useEffect(() => {
async function sendData() {
const res = await fetch('http://www.baidu.com')
.then(response => response.json())
.then(data => console.log(data))
}
sendData()
},[])
而 useLayoutEffect 的作用和 useEffect 几乎差不多,几乎看不到任何差别,但它们的渲染底层逻辑就稍微不同。
具体可见这篇文章,用一个例子很清楚讲明,在性能优化上尽量选用useEffect,在解决页面渲染更新会闪烁可以用useLayoutEffect。
步骤:导入useRef函数,通过ref绑定要获取的元素,执行useRef并传入null,这将会返回一个含有current属性的对象。
import React, { useRef, useEffect } from 'react'
//app组件里绑定
<UseRef1 ref={hook} />
<p ref={pRef}>useRef学习,标签实例</p>
//获取
const pRef = useRef(null)
const hook = useRef(null)
useEffect(() => {
console.log(pRef.current)
console.log(hook.current.state.name)
}, [])
算是对 Provider
和 Consumer
写法的一种优化吧,不同的是 useContext
可以将传递的数据嵌套的更深,孙组件都能拿到父组件的值,什么意思呢?
大致就是Provider
只能给 Consumer
共享数据, Consumer
下的孙组件却不行!但 useContext
却可以!
function App(){
//父最组件用Provider为所有子孙提供value值
return (
<numberContext.Provider value={12}>
<div>
<Son/>
</div>
</numberContext.Provider>
)
function Son(){
//子组件使用Consumer从父组件那上下文获取value
return(
<numberContext.Consumer>
{value=><div>the answer is {value}</div>}
</numberContext.Consumer>
)
}
Provider
和 Consumer
在标准模式下相当于消息的发布订阅模式了,这很好理解。 useContext
则可以理解为小型的 redux
。
一个例子直接说明梗概。
import React, { createContext, useContext, useState } from 'react'
//1.先创建个上下文对象
const Context = createContext()
//2.在顶层app组件上,使用Providr包裹孙组件,value传值,这都是必要的
// 和之前的 Provider + Consumer 没啥区别,
const [count,setCount]=useState(10)
<Context.Provide value={count}>
<div>
<Son />
<button onClick={()=>{setCount(count+1)}}></button>
</div>
</Context.Provider>
// 不同的是这Son组件下包含Shun组件 ->
//3.在Shun组件里调用useContext方法,参数用父组件createContext创建出来的对象
//Context和createContext保存的变量一致
const count = useContext(Context)
function Shun(){
//子组件使用Consumer从父组件那上下文获取value
return(
<numberContext.Consumer>
the answer is {value}
</numberContext.Consumer>
)
}
//这样孙组件就可以显示或同步app组件的值
总结:就是父组件多了用 createContext
创建 context
对象的步骤,要用到的组件少了 Consumer
的引用,多了个 useContext
的调用,没了,就这么简单。
const [ state,dispatch ] = useReducer(reducer, initState, init)
接收三个参数,分别为:处理状态更新的reducer,状态初始值,状态初始化函数。
有两种传递方式:不传第三个参数,如1;state数据比较复杂,可以将第二个参数作为第三个参数传入,如2。
最后,useReducer将返回两个东西,一个当前最新的状态state,一个是更新状态的dispatch。
//1.将状态初始值作为第二个参数传入
const [state, dispatch] = useReducer(
reducer,
{count: initState}
)
//2.惰性初始化,将第二个参数作为第三个函数传入
function init(initState){
return {count: initState}
}
function renducer(state,action){
switch{
case "change":
return {count: state.count - 1}
case "reset":
return init(ac)
//...
}
}
function Counter({initState}){
const [ state,dispatch ] = useReducer(reducer, initState, init)
return(
<>
<button onClick={() => dispath({type: 'change',count: initState})}></button>
</>
)
}
入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。
那为啥要用useCallback:那是因为在函数组件里的函数,会随着状态值的更新而重新渲染,函数也会频繁被定义且组件通信很耗性能。使用useCallback+memo可以解决上述问题
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。
入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。如果没有提供依赖项数组,useMemo
在每次渲染时都会计算新的值。
官方建议:不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect
的适用范畴,而不是 useMemo
。
痛点见上述
//使用Memo后确实不会影响了,但父组件传值过来呢
const Son = React.memo(function Son(){
...
})
//父组件:这样就不会
const childClick = useCallback(() => {}.[])
<Son click={childClick} />
先下载react-router-dom@6依赖,在导入import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
, 基本使用如下。BrowserRouter:包裹整个路由,一个react应用只需使用一次,还有个路由模式:hashRouter
<BrowserRouter>
<Link to="/">index</Link>
<p></p>
<Link to="about">about</Link>
<Routes>
<Route path="/" element={<Index />}></Route>
<Route path="about" element={<About />}></Route>
</Routes>
</BrowserRouter>
编程式导航:replace设置为true表示不保留历史记录
import { useNavigate } from 'react-router'
const navigage = useNavigate()
const goAbout = () => {
navigage('/about', { replace: true })
}
<button onClick={goAbout}>跳转到关于我</button>
编程式导航传参方式:searchParams / params
//searchParams 传参:和取参
navigage('/about?id=0001', { replace: true })
let params = useSearchParams()
let id = params.get('id')
// params 传参:和取参
navigage('/about/id/0001', { replace: true })
let params = useParams()
let id = params.id
二级路由:如果要设置一级路由默认渲染的二级路由,可如下例所示
//给URL地址about后面添加nextRouter二级路由
//在About组件内引入Outlet,再设置 出口即可
<Route path="about" element={<About />}>
{/* 二级路由 */}
<Route path="nextRouter" element={<NextRouter />}></Route>
{/* 默认二级路由 */}
<Route index element={<Board />}></Route>
{/* 404页面 */}
<Route path="*" element={<NotFound />}></Route>
</Route>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lep4BGR-1658071112905)(C:\Users\只属于她\AppData\Roaming\Typora\typora-user-images\image-20220627223718081.png)]
标签传递 Boolean值需要按对象形式传递,如 isTrue= {false},而非isTrue= “false”
可以传递任何数据,包括函数,jsx
给组件添加内置属性或方法
//添加属性验证
Module.protoTypes={
title:Name.string
isTrue:flag.bool
}
添加校验规则:需要另外安装第三方插件npm install prop-type
,导入prop-types
包,在组件名.propTypes={}
添加校验规则。
可检验的规则有:
import Proptypes from 'prop-types'
//父组件调用
<Son list={9}>
//子组件
Son.propTypes={
list:PropTypes.arr
}
#### 获取输入框value值的几种方法
state
中定义变量,在输入框绑定value={this.state.component} ,和 onChange={this.changeInputValue} 事件
,通过事件修改state的中变量。 changeInputValue = (e) => {
this.setState({
component: e.target.value,
})
}
myRef = React.createRef()
,在input标签内绑定ref={this.myRef}
,最后通过this.myRef.current.value
拿到value值。删除:直接filter过滤传过来的值不等于数组的值就好,或者根据索引 splice(index, 1)
item项
this.setState({ list: this.state.list.filter((item) => item.id !== id) })
修改:一般对整个数组map遍历到需要修改的项
list: this.state.list.map((item) => {
if (item.id === id) {
return {
attitude: attitude === 1 ? 0 : 1,
}
} else {
return item
}
}),