第三十七章 扩展知识点 1:https://developer.aliyun.com/article/1409375
6、Fragment 和空标签(<> 和 </>)
在 React
中,Fragment
和空标签(<>
和 </>
)都用于组合多个子元素,而不需要添加额外的 DOM
元素。这在避免不必要的 DOM
层次结构和提高性能方面非常有用。
使用 Fragment
要使用 Fragment
,您需要从 react
包中导入它,然后将它作为包裹元素。以下是一个示例:
import React, { Fragment } from 'react'; function MyComponent() { return ( <Fragment> <div>Element 1</div> <div>Element 2</div> </Fragment> ); }
在这个示例中,Element 1
和 Element 2
将作为同级元素渲染,而不需要额外的包裹元素。
使用空标签
空标签(<>
和 </>
)是 Fragment
的简写语法。它们的功能与 Fragment
相同,但更简洁。以下是一个使用空标签的示例:
import React from 'react'; function MyComponent() { return ( <> <div>Element 1</div> <div>Element 2</div> </> ); }
在这个示例中,Element 1
和 Element 2
也将作为同级元素渲染,而不需要额外的包裹元素。
请注意,虽然空标签更简洁,但它们不支持添加 key
属性。如果您需要在迭代中使用 Fragment
并添加 key
属性,您应该使用完整的 <Fragment>
标签。
7、Context
在 React
中,Context
是一种在组件树中传递数据的方法,而无需通过每个中间组件显式传递 props
。这在处理跨越多个层次的全局数据(如主题、语言设置或用户信息)时非常有用。
(1)、创建 Context
首先,您需要创建一个 Context
。通常,您会在应用程序的顶层创建一个 Context
。
import React from 'react'; const MyContext = React.createContext(); export default MyContext
(2)、使用Provider传递Context值
要在组件树中提供 Context
值,您需要使用 Provider
组件。Provider
接受一个 value
属性,该属性将作为 Context
的值传递给所有使用该 Context
的子组件。
import React, { Component } from 'react' import B from './B' import MyContext from './MyContext' const {Provider} = MyContext export default class Demo extends Component { render() { return ( <div> <Provider value={{username:'tom',age:18}}> <B/> </Provider> </div> ) } }
(3)、在类式组件中使用static contextType接收Context值
在类组件中访问 Context
的方法是使用 static contextType
。将 contextType
设置为您要访问的 Context
,然后在组件中,您可以通过 this.context
访问 Context
的值。
import React, { Component } from 'react' import MyContext from '../MyContext' import C from '../C' import D from '../D' export default class B extends Component { static contextType = MyContext render() { console.log('我是B组件接收的context',this.context) return ( <div> <h2>我是B组件{this.context.username+','+this.context.age}</h2> <C/> <D/> </div> ) } }
(4)、使用Customer在函数组件和类组件中接收Context值
在类组件和函数组件中,您可以使用 Context.Consumer
组件来访问 Context 值。Context.Consumer
接受一个函数作为子元素,该函数接收 Context
的值作为参数。
import MyContext from "../MyContext" const {Consumer} = MyContext export default function C () { return ( <div> <Consumer> {value => { console.log('我是C组件接收的Context',value) return <h3>我是C组件{value.username+''+value.age}</h3> }} </Consumer> </div> ) }
(5)、使用useContext Hook在函数组件中接收Context值
要在组件中使用 Context
值,您可以使用 useContext Hook
。useContext
接受一个 Context
对象,并返回该 Context
的当前值。
import { useContext } from 'react' import MyContext from '../MyContext' export default function D () { const contextValue = useContext(MyContext) console.log('我是D组件接收的context',contextValue) return ( <div> <h3>我是D组件</h3> <div>{contextValue.username+','+contextValue.age}</div> </div> ) }
请注意,虽然 Context
可以简化跨多个层次的数据传递,但在不需要的情况下过度使用 Context
可能会导致组件之间的不必要的耦合。因此,建议仅在确实需要全局数据时使用 Context
,在应用开发中一般不用Context
, 一般都用它的封装react
插件
8、组件优化
Component的2个问题
- 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
- 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
效率高的做法
只有当组件的state或props数据发生改变时才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
解决
办法1: 重写shouldComponentUpdate()方法 比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false 办法2: 使用PureComponent PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true 注意: 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false 不要直接修改state数据, 而是要产生新数据 项目中一般使用PureComponent来优化
如何理解pureComponent
我们首先需要了解React
组件的基本概念。在React
中,有两种类型的组件:函数组件和类组件。PureComponent
是类组件的一种特殊类型,它可以帮助我们优化性能。
PureComponent
是React
提供的一个基类,它继承自React.Component
。与普通的Component
不同,PureComponent
实现了一个浅比较(shallow comparison
)的shouldComponentUpdate
方法。这意味着,当组件的props
或state
发生变化时,PureComponent
会对新旧props
和state
进行浅比较,如果它们相等,那么组件就不会重新渲染。这可以避免不必要的渲染,从而提高性能。
要使用PureComponent
,只需将组件的基类从React.Component
更改为React.PureComponent
。例如:
import React from 'react'; class MyComponent extends React.PureComponent { // ... }
需要注意的是,PureComponent
的浅比较可能会导致一些问题。例如,当组件的props
或state
包含嵌套对象时,浅比较可能无法检测到嵌套对象的变化。因此,在使用PureComponent
时,需要确保组件的props
和state
是不可变的(immutable
)。
总之,PureComponent
是一种特殊的React类组件,它通过实现浅比较的shouldComponentUpdate
方法来避免不必要的渲染,从而提高性能。在使用PureComponent
时,需要确保组件的props
和state
是不可变的
使用shouldComponentUpdate进行自定义优化
如果PureComponent
或React.memo
的浅比较不足以满足需求,可以在类组件中实现自定义的shouldComponentUpdate
方法。这允许您根据特定条件决定组件是否应该重新渲染。例如:
class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { // 自定义比较逻辑 return nextProps.someProp !== this.props.someProp; } // ... }
React.memo
是React
库中的一个高阶组件(Higher-Order Component,简称HOC),它用于优化函数组件的性能。React.memo
的作用类似于类组件中的PureComponent
,它通过对组件的props进行浅比较(shallow comparison
)来避免不必要的重新渲染。
当使用React.memo
包装一个函数组件时,只有当组件的props
发生变化时,组件才会重新渲染。这可以帮助我们减少不必要的渲染,从而提高性能。例如:
import React from 'react'; const MyComponent = React.memo(function MyComponent(props) { // ... });
需要注意的是,React.memo
的浅比较可能会导致一些问题。例如,当组件的props
包含嵌套对象时,浅比较可能无法检测到嵌套对象的变化
9、render props
如何向组件内部动态传入带内容的结构(标签)?
Vue中: 使用slot技术, 也就是通过组件标签体传入结构 <A><B/></A> React中: 使用children props: 通过组件标签体传入结构 使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
<A> <B>xxxx</B> </A> {this.props.children} 问题: 如果B组件需要A组件内的数据, ==> 做不到
标签体是一种React
模式,它允许您将一个或多个React
元素作为组件的子元素传递,并在组件中渲染这些元素。这些子元素可以是任何有效的React
元素,包括其他组件。例如:
function MyComponent(props) { return ( <div> {props.children} </div> ); } function App() { return ( <MyComponent> <div>Child 1</div> <div>Child 2</div> </MyComponent> ); }
在这个例子中,MyComponent
接受一个名为children
的prop
,该prop
是一个React
元素或一组React
元素。在MyComponent
中,我们使用props.children
来渲染这些子元素。在App
组件中,我们将两个<div>
元素作为MyComponent
的子元素传递。这些元素将被渲染为MyComponent
的子元素。
render props
<A render={(data) => <C data={data}></C>}></A> A组件: {this.props.render(内部state数据)} C组件: 读取A组件传入的数据显示 {this.props.data}
render props
是另一种React
模式,它允许您将一个函数作为prop
传递给组件,并在组件中调用该函数以渲染子元素。这个函数通常称为render
或children
。例如:
function MyComponent(props) { const { render } = props; const data = { /* some data */ }; return ( <div> {render(data)} </div> ); } function App() { return ( <MyComponent render={(data) => ( <div> {/* render something based on the data */} </div> )} /> ); }
在这个例子中,MyComponent
接受一个名为render
的prop
,该prop
是一个函数。在MyComponent
中,我们调用render
函数并将数据作为参数传递。render
函数返回一个React
元素,该元素用于渲染MyComponent
的子元素。
总之,标签体和render props
是两种不同的React
模式,它们的作用和用法也有所不同。标签体用于将React
元素作为组件的子元素传递,而render props
用于将函数作为prop
传递,并在组件中调用该函数以渲染子元素。这两种模式都可以用于实现组件的可重用性和灵活性
10、 错误边界
理解:
错误边界(Error boundary
):用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。
使用方式:getDerivedStateFromError
配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发 static getDerivedStateFromError(error) { console.log(error); // 在render之前触发 // 返回新的state return { hasError: true, }; } componentDidCatch(error, info) { // 统计页面的错误。发送请求发送到后台去 console.log(error, info); }
对于React
应用程序中的错误处理,可以使用错误边界来捕获和处理组件中的错误。错误边界是一种React
组件,它可以捕获其子组件中的错误,并显示备用UI
而不是崩溃的UI
。以下是一个简单的错误边界组件的示例:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, errorInfo) { // You can also log the error to an error reporting service console.error(error, errorInfo); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>服务器异常,请稍后在试.</h1>; } return this.props.children; } }
在这个例子中,ErrorBoundary
是一个错误边界组件。它通过getDerivedStateFromError
和componentDidCatch
方法来捕获子组件中的错误,并在发生错误时显示备用UI
。
getDerivedStateFromError
方法返回一个对象,该对象将更新组件的状态以显示备用UI
。componentDidCatch
方法可以用于记录错误或将错误发送到错误报告服务。
要使用错误边界,只需将其包装在可能会发生错误的组件周围即可。例如:
<ErrorBoundary> <MyComponent /> </ErrorBoundary>
在这个例子中,MyComponent
是可能会发生错误的组件。通过将MyComponent
包装在ErrorBoundary
中,我们可以捕获并处理MyComponent
中的错误。
案例:我们在render
里面写一段错误代码,render
属于生命周期
import React, { Component } from 'react' export default class Demo extends Component { state = {users:'users'} render() { return ( <div> 我是Demo组件 { this.state.users.map((user)=>{ return <h1>{user.name}</h1> }) } </div> ) } }
查看效果:
11、组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
1.props: (1).children props (2).render props 2.消息订阅-发布: pubs-sub、event等等 3.集中式管理: redux、dva等等 4.conText: 生产者-消费者模式
比较好的搭配方式:
父子组件:props 兄弟组件:消息订阅-发布、集中式管理 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)