关于React的这些常用技巧,你应该知道

举报
CoderBin 发表于 2022/11/04 18:05:05 2022/11/04
【摘要】 大家好,我是CoderBin,本文总结了React中的一些常用技巧,相信看完本文的小伙伴都能从中有所收获💪。 创作不易,你们的点赞收藏留言就是我最大的动力💓 如果文中有不对、疑惑的地方,欢迎各位小伙伴们在评论区留言指正🌻

前言

大家好,我是CoderBin,本文总结了React中的一些常用技巧,相信看完本文的小伙伴都能从中有所收获💪。

创作不易,你们的点赞收藏留言就是我最大的动力💓

如果文中有不对、疑惑的地方,欢迎各位小伙伴们在评论区留言指正🌻

一、 setState

1. setState更新状态的2种写法

  1. setState(stateChange, [callback])------对象式的 setState
    • 1.stateChange 为状态改变对象(该对象可以体现出状态的更改)
    • 2.callback 是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
  2. setState(updater, [callback])------函数式的 setState
    • 1.updater 为返回 stateChange 对象的函数。
    • 2.updater 可以接收到 state 和 props。
    • 3.callback 是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

2. 代码示例

import React, { Component } from 'react'

export default class SetState extends Component {

  state = { count: 0 }

  increment = () => {
    // !组件的状态更新是异步的
    // * 对象形式的 setState
    // const { count } = this.state
    // // setState() 第二个参数为render执行之后的回调
    // this.setState({ count: count + 1 }, () => {
    //   console.log(this.state.count);
    // })

    // * 函数式的 setState
    // setState(updateFn(state,props), cb) , 第二个参数cb为render执行之后的回调
    this.setState((state, props) => {
      // state 就是维护的状态,props为组件接收的属性值
      // console.log(state, props);
      return { count: state.count+1 }
    },)
  }

  render() {
    return (
      <div>
        <h2>当前求和为:{this.state.count}</h2>
        <button onClick={this.increment}>点击+1</button>
      </div>
    )
  }
}

3. 总结

  1. 对象式的 setState 是函数式的 setState 的简写方式(语法糖)
  2. 使用原则:
    1. 如果新状态不依赖于原状态 ===> 使用对象方式
    2. 如果新状态依赖于原状态 ===> 使用函数方式
    3. 如果需要在 setState() 执行后获取最新的状态数据, 要在第二个 callback 函数中读取

二、lazyLoad 组件懒加载

1. 路由组件的lazyLoad

import React, { Component, lazy, Suspense } from 'react'
import { NavLink, Route  } from 'react-router-dom'

//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))

export default class Lazyload extends Component {
  render() {
    return (
      <div>
        {/* 编写路由链接 */}
        <NavLink to='/home'>home</NavLink>&nbsp;&nbsp;
        <NavLink to='/about'>about</NavLink>

        {/* 注册路由 */}
        {/*2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面*/}
        {/* fallback 表示在跳转的过程中,展示的东西,可以是标签,或者一个组件(必须是正常引入的组件) */}
        <Suspense fallback={<h1>loading...</h1>}>
          <Route path="/home" component={Home}/>
          <Route path="/about" component={About}/>
        </Suspense>
        
      </div>
    )
  }
}

2. 总结

  1. 路由懒加载必须借助 react 中的 lazy 函数和 Suspense 组件
  2. 路由的导入方式:const Home = lazy(() => import('./Home'))
  3. 注册路由要用 <Suspense></Suspense> 包裹

三、Hooks

1. React Hook/Hooks是什么?

  1. Hook是React 16.8.0版本增加的新特性/新语法
  2. 可以让你在函数组件中使用 state 以及其他的 React 特性

2. 三个常用的Hook

  1. State Hook: React.useState()
  2. Effect Hook: React.useEffect()
  3. Ref Hook: React.useRef()
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()

3. State Hook

  1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
  2. 语法: const [xxx, setXxx] = React.useState(initValue)
  3. useState()说明:
    • 参数: 第一次初始化指定的值在内部作缓存
    • 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
  4. setXxx()2种写法:
    • setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    • setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

代码示例

import React from 'react'

export default function Hook() {

  // count 为初始值,setCount 为改变初始值的函数
  // * 1. 为每一个状态单独开一个 useState()
  // (官方建议为每一个状态单独进行管理,不推荐使用对象形式)
  const [count, setCount] = React.useState(0)

  // 点击 + 1
  const increment = () => {
    // 直接传入参数调用,就可以改变初始值
    setCount(count + 1) // 第一种方式
    // setCount((count) => count + 1) // 第二种方式
  }

  return (
    <div>
      <h2>当前求和为:{ count }</h2>
      <button onClick={increment}>点击+1</button>
    </div>
  )
}

4. Effect Hook

  1. Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

  2. React中的副作用操作:发ajax/axios请求数据获取、设置订阅 / 启动定时器、手动更改真实DOM

  3. 语法和说明:

    useEffect(() => {
        // 在此可以执行任何带副作用操作
        return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        }
    }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
  4. 可以把 useEffect Hook 看做如下三个生命周期函数的组合

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

5. Ref Hook

  1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
  2. 语法: const refContainer = useRef()
  3. 作用:保存标签对象,功能与 React.createRef() 一样

代码示例

import React from 'react'

export default function Hook() {
  const [input, setInput] = React.useState('')
  
  // * useRef(),创建一个容器,与类式组件中的createRef相同的用法
  const myRef = React.useRef()
  // 展示输入的数据
  function show(e) {
    setInput(e.target.value)
  }
    
  return (
    <div>
      <h2>你的输入:{input }</h2>
       {/* ref 的值即为上面创建的 myRef 容器*/}
      <input type="text" ref={myRef} onInput={show} placeholder='请输入'/>
    </div>
  )
}

四、Fragment

1. 作用

可以不用必须有一个真实的DOM根标签了

2. 代码示例

import React, { Component, Fragment } from 'react'

export default class FragmentDemo extends Component {
  render() {
    return (
      // 作用:可以不用必须有一个真实的DOM根标签了
      // <></> 空标签也可以
      // 如果要遍历,就使用Fragment,因为可以添加key值
      <Fragment key={1}>
        <input type="text" />
        <input type="text" />
      </Fragment>
    )
  }
}

五、Context 上下文对象

1.理解

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

2. 使用

  1. 创建Context容器对象:const XxxContext = React.createContext()

  2. 渲染子组件时,外面包裹xxxContext.Provider, 通过 value 属性给后代组件传递数据:

    <xxxContext.Provider value={数据}>
        子组件
    </xxxContext.Provider>
    
  3. 后代组件读取数据:

    
    //第一种方式:仅适用于类组件 
    static contextType = xxxContext  // 声明接收context
    this.context // 读取context中的value数据
    
    //第二种方式: 函数组件与类组件都可以
    <xxxContext.Consumer>
        {
        value => ( // value就是context中的value数据
        要显示的内容
        )
    }
    </xxxContext.Consumer>
    

3. 代码示例

import React, { Component } from 'react'
import './index.css'

// 创建 Context 容器组件
const MyContext = React.createContext()

// 父组件
export default class A extends Component {
  state = {
    username: 'coderbin',
    age: 21
  }
  render() {
    const { username, age} = this.state
    return (
      <div className='parent'>
        <h2>我是 A 组件</h2>
        <h3>我的用户名是:{this.state.username}</h3>
        {/* 注意:这里必须通过 value属性向后代传递数据 */}
        <MyContext.Provider value={{username, age}}>
          <B />                
        </MyContext.Provider>
      </div>
    )
  }
}

// 子组件
class B extends Component{
  render() {
    return (
      <div className='child'>
        <h2>我是 B 组件</h2>
        <C />    
      </div>
    )
  }
}

// 孙组件
// 类式组件接收数据的方法
// class C extends Component{
//   // 声明接收 context
//   static contextType = MyContext
//   render() {
//     const {username, age} = this.context
//     return (
//       <div className='grand'>
//         <h2>我是 C 组件</h2>
//         <h3>我是从A组件接收到的用户名:{username},年龄是:{ age }</h3>
//       </div>
//     )
//   }
// }

// 函数式组件接收数据的方法
// 使用 <MyContext.Consumer> 包裹,在里面接收展示数据
function C() {
    return (
      <div className='grand'>
        <h2>我是 C 组件</h2>
        <h3>我是从A组件接收到的用户名:
          <MyContext.Consumer>
            {value => `${value.username},年龄是:${value.age}`}
          </MyContext.Consumer>
        </h3>
      </div>
    )
}

4. 注意

在应用开发中一般不用context, 一般都用它的封装react插件

六、组件优化

1. Component的2个问题

  1. 只要执行 setState(),即使不改变状态数据, 组件也会重新 render() ==> 效率低
  2. 只要当前组件重新 render(), 就会自动重新 render 子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

2. 效率高的做法

  • 只有当组件的 state 或 props 数据发生改变时才重新 render()

3. 原因

  • Component 中的 shouldComponentUpdate() 总是返回 true

4. 解决

  1. 办法1:
    • 重写 shouldComponentUpdate() 方法
    • 比较新旧 state 或 props 数据, 如果有变化才返回 true, 如果没有返回 false
  2. 办法2:
    • 使用 PureComponent
    • PureComponent 重写了 shouldComponentUpdate(), 只有 state 或 props 数据有变化才返回 true
  3. 注意
    • 只是进行 state 和 props 数据的浅比较, 如果只是数据对象内部数据变了, 返回 false
    • 不要直接修改 state 数据, 而是要产生新数据
    • 项目中一般使用 PureComponent 来优化

5. 代码示例

import React, { PureComponent } from 'react'
import './index.css'

// PureComponent 重写了 shouldComponentUpdate(),
// 只有 state 或 props数据有变化才返回true

// 注意:
// 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
// 不要直接修改state数据, 而是要产生新数据
  
export default class Parent extends PureComponent {
  state = {
    carName: '奔驰'
  }

  changeCar = () => {
    this.setState({
      carName: '法拉利'
    })
  }

  // shouldComponentUpdate(nextProps, nextState) {
  //   // 当前值
  //   console.log(this.props, this.state);  
  //   // 接下来要变化的目标 Props,目标 State
  //   console.log(nextProps, nextState);
  //   return true
  // }

  render() {
    console.log('Parent -- render');
    const { carName } = this.state
    return (
      <div className='parent'>
        <h3>我是 Parent 组件</h3>
        <span>我的车的名字是:{carName}</span><br />
        <button onClick={this.changeCar}>点击换车</button>
        <Child carName={carName} />
      </div>
    )
  }
}

class Child extends PureComponent {
  render() {
    console.log('Child -- render');
    return (
      <div className='child'>
        <h3>我是 Parent 组件</h3>
        <span>我接收到的车是:{ this.props.carName }</span>
      </div>
    )
  }
}

七、render props

1. 如何向组件内部动态传入带内容的结构(标签)?

Vue中:

  • 使用 slot 插槽技术, 也就是通过组件标签体传入结构 <A><B/></A>

React中:

    1. 使用 children props: 通过组件标签体传入结构
    1. 使用 render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

2. children props

  1. 父组件: <A> <B>xxxx</B></A>
  2. 子组件:{this.props.children}

问题:如果 B 组件需要 A 组件内的数据, ==> 做不到

3. render props

  1. 最外层组件:<A render={(data) => <B data={data}></B>}></A>
  2. A组件:{this.props.render(内部state数据)}
  3. B组件:读取A组件传入的数据显示 {this.props.data}

4. 代码示例

import React, { Component } from 'react'
import './index.css'

export default class RenderProps extends Component {

  render() {
    return (
      <div className='parent'>
        <h3>我是 Parent 组件</h3>
        {/* 1. 给 A 组件一个 render 属性,值为一个函数,里面返回 B 组件 */}
        {/* 2. 这个函数可以接受一个来自 A 组件的参数,传递给 B 组件 */}
        <A render={(name) => <B name={name} />} />
      </div>
    )
  }
}

class A extends Component {
  state = { name: 'coderbin'}
  render() {
    const { name } = this.state
    return (
      <div className='a'>
        <h3>我是 A 组件</h3>
        {/* 1. 接收传过来的 render,直接调用即可渲染 B 组件*/}
        {/* 2. 调用的时候可以传递一个数据 */}
        {this.props.render(name)}
      </div>
    )
  }
}

class B extends Component {
  render() {
    console.log('接收到了 A 组件传递的数据:',this.props.name);
    return (
      <div className='b'>
        <h3>我是 B 组件</h3>
      </div>
    )
  }
}

八、错误边界

1. 理解

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

2. 特点

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

3. 使用方式

两个生命周期函数:getDerivedStateFromError 配合 componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}

componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

4. 代码示例

Child.jsx

import React, { Component } from 'react'

export default class Child extends Component {
	state = {
		users:[
			{id:'001',name:'tom',age:18},
			{id:'002',name:'jack',age:19},
			{id:'003',name:'peiqi',age:20},
		]
         // 测试错误的情况
		// users:'abc'
	}

	render() {
		return (
			<div>
				<h2>我是Child组件</h2>
				{
					this.state.users.map((userObj)=>{
						return <h4 key={userObj.id}>{userObj.name}----{userObj.age}</h4>
					})
				}
			</div>
		)
	}
}

Parent.jsx

import React, { Component } from 'react'
import Child from './Child'

export default class Parent extends Component {

	state = {
		hasError:'' //用于标识子组件是否产生错误
	}

	//当 Parent的子组件出现报错时候,会触发 getDerivedStateFromError调用,并携带错误信息
	static getDerivedStateFromError(error){
		console.log('@@@',error);
		return {hasError:error}
	}

  // 生命周期函数:组件挂载过程中,如果子组件出了错误,就会调用这个钩子
	componentDidCatch(){
		console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
	}

	render() {
		return (
			<div>
				<h2>我是Parent组件</h2>
				{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}
			</div>
		)
	}
}

九、组件通信方式总结

1. 组件间的关系:

  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)

2. 几种通信方式:

  1. props:
    • children props
    • render props
  2. 消息订阅-发布:pubs-sub、event等等
  3. 集中式管理:redux、dva等等
  4. conText:生产者-消费者模式

3. 比较好的搭配方式:

  1. 父子组件:props
  2. 兄弟组件:消息订阅-发布、集中式管理
  3. 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

每文一句:立志宜思真品格,读书须尽苦功夫。

本文列举了对 React 的一些常用写法技巧。当然,关于 React 的这部分知识远远不仅如此,React 中我们需要掌握的知识还非常多。在后续的文章中也会继续跟各位分享更多的 React 知识。有疑问的小伙伴可以在评论区留言,大家一起探讨学习!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。