由Vue转React的自学文档

react学习文档

由vue3转学react,之前从未接触过react,自学了一遍感觉还好,容易上手。
期间的一些笔记,后续有更深的见解持续更新。

组件(类组件,函数组件)

  • 组件名称首字母要大写,必须有返回值,没有写null
  • 设置默认值:function 组件名({默认值:1})/ 类组件:static defaultProps ={ 默认值:1 }
  • 实例化:类组件有实例化,函数组件没有

组件通信

父传子

  1. 父组件通过state传递值。state里设置要传递的值
  2. 给子组件标签添加属性,msg={this.state.msg1}
  3. 子组件通过props接受父组件传来的数据(类组件用this.props /函数组件通过参数)获取 ->props对象

子传父

​ 子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数实参传入即可

兄弟组件通信

​ 利用共同的父组件,实现子传父,父在传子的兄弟通信

跨组件(祖孙)通信Context

​ 在嵌套组件树里,采用Context不需要通过层层props传递,就能进行数据传递。

  1. 创建Context对象,导出Provider和Consumer对象
  2. 使用Provider包含根组件提供数据
  3. 将需要用到组件,使用Consumer包裹,然后 value => 获取
//祖组件
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>
    )
}

生命周期

在这里插入图片描述

挂载阶段:

  1. constructor(初始化state,创建Ref,使用bind解决this问题) ,初始化最先执行,只执行一次。

  2. render (渲染UI),每次组件渲染的时候都会触发。

  3. componentDidMount(发送网络请求,Dom操作),完成渲染后执行,初始化的时候执行一次。

更新阶段:

  1. render(与挂载相同,是同一个render),每次组件都会触发
  2. componentDidUpdate(Dom操作,可以获取到更新后的Dom,不要直接调用setState),更新后渲染

卸载阶段

  1. componentWillUnmount(执行清理工作,比如清理定时器),组件卸载

hooks的一些API:

本质:让函数组件更强大更灵活的钩子,只能在函数组件(有状态)中使用。

作用:组件状态复用,class组件自身问题

注意:只能写在函数组件最外层,不能写在if,for循环判断里面(官文文档解释为react的运行机制要保持这些hooks的顺序的唯一性),可以将回调函数作为参数传递

useState

const [count, setCount] = useState(initCount);

useState传入的是当前count初始值也就是initCount(可以是初始值也可以是函数),然后返回的是最新的count,和一个修改count的方法(可以普通调用和函数调用,数据是对象类型的话,一般要结合拓展运算符复制一份)。

//三种情况
setCount(count + 1)
setCount(() => count + 1)
setCount(() => {
    return{
        ...count,
        //再对象赋值
    }
})

初始值initCount只有在首次渲染生效,后续更新会被忽略,直接调用setCount

一句话:在同步里,异步更新状态和dom,在异步里,同步更新状态和dom。

useEffect:为更好处理副作用(除了更新UI外的作用,如ajax,本地存储)

感觉像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:获取实例dom或组件方法,必须是类组件

步骤:导入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)
  }, [])
useContext:祖孙组件响应式更新

算是对 ProviderConsumer 写法的一种优化吧,不同的是 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>
  )
}

ProviderConsumer 在标准模式下相当于消息的发布订阅模式了,这很好理解。 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组件 <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 的调用,没了,就这么简单。

useReducer

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>    
    </>
    )
}
useCallback

入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。

那为啥要用useCallback:那是因为在函数组件里的函数,会随着状态值的更新而重新渲染,函数也会频繁被定义且组件通信很耗性能。使用useCallback+memo可以解决上述问题

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

useMemo(相当于vue的computed)

入参和useEffect,接收两个参数(fun,[]),一个处理函数,另一个关联的状态或数组,这个变了就重新执行。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

官方建议:不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo

useCallback + useMemo

痛点见上述

//使用Memo后确实不会影响了,但父组件传值过来呢
const Son = React.memo(function Son(){
    ...
})

//父组件:这样就不会
const childClick = useCallback(() => {}.[])
<Son click={childClick} />

路由:react-router-dom

先下载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,再设置<Outlet />出口即可
<Route path="about" element={<About />}>
  {/* 二级路由 */}
  <Route path="nextRouter" element={<NextRouter />}></Route>
  {/* 默认二级路由 */}
  <Route index element={<Board />}></Route>
  {/* 404页面 */}
  <Route path="*" element={<NotFound />}></Route>
</Route>

mobx

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lep4BGR-1658071112905)(C:\Users\只属于她\AppData\Roaming\Typora\typora-user-images\image-20220627223718081.png)]

校验规则第三方插件: prop-type

标签传递 Boolean值需要按对象形式传递,如 isTrue= {false},而非isTrue= “false”

可以传递任何数据,包括函数,jsx

给组件添加内置属性或方法

//添加属性验证
Module.protoTypes={
	title:Name.string
    isTrue:flag.bool
}

添加校验规则:需要另外安装第三方插件npm install prop-type ,导入prop-types包,在组件名.propTypes={} 添加校验规则。

可检验的规则有:

  • 常见类型array、bool、fun、number、obj、string
  • 基本类型:element
  • 必填项:isRequired
  • 特定结构对象:shape({})
import Proptypes from 'prop-types'
//父组件调用
<Son list={9}>
//子组件
Son.propTypes={
   list:PropTypes.arr
}

其他

#### 获取输入框value值的几种方法

  1. 通过在state 中定义变量,在输入框绑定value={this.state.component} ,和 onChange={this.changeInputValue} 事件,通过事件修改state的中变量。
  changeInputValue = (e) => {
    this.setState({
      component: e.target.value,
    })
  }
  1. 通过ref拿到。先声明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
	}
}),
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值