草庐IT

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

CoderBin 2023-03-28 原文

前言

大家好,我是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={loading...}> <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 知识。有疑问的小伙伴可以在评论区留言,大家一起探讨学习!

有关关于React的这些常用技巧,你应该知道的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. 动漫制作技巧如何制作动漫视频 - 2

    动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、

  5. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

  6. ruby - EventMachine - 你怎么知道你是否落后了? - 2

    我正在研究使用EventMachine支持的twitter-streamruby​​gem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@

  7. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  8. ruby-on-rails - 关于 Ruby 的一般问题 - 2

    我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia

  9. ruby-on-rails - Ruby 长时间运行的进程对队列事件使用react - 2

    我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby​​脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几

  10. ruby-on-rails - 我现在(2010 年 1 月)应该使用哪个版本的 Ruby? - 2

    我有1.8.6附带的VanillaMacOSXLeopard。我是RoR的新手,所以会学习网上的教程。在使用更高版本的Ruby时,我是否可能会发现遵循它们的问题?我目前正在查看提到1.8.6和1.8.7的这个-http://www.railstutorial.org/book 最佳答案 RoR教程对两者都适用,但如果您正在学习Ruby,则应该学习1.9。Rails3将不支持1.8.6,所以我会选择1.8.7或1.9。我还推荐使用RVM在Ruby版本之间切换。 关于ruby-on-rail

随机推荐