聪明式组件VS傻瓜式组件
聪明式组件是操作数据的组件,傻瓜式组件是只展示数据的组件
我们先写一个聪明式CommentList组件:
class CommentList extends Component {
constructor(props) {
super(props);
this.state = {
comments: [{id: 1,author: 'facebook',content: 'react好'}]
}
}
componentDidMount () {
setTimeout(() => {
this.setState({
comments: [{id: 2,author: '尤雨溪', content: 'vue更好'},]
})
}, 1000);
}
render () {
return (
<div>
{this.state.comments.map((item, i) => {
return (<Comment key={item.id} comments={item}></Comment>)})}
</div>
);
}
}
在聪明式组件CommentList中对数据进行操作,然后在Comment傻瓜式组件对数据进行展示。
Comment傻瓜式组件
function Comment (props) {
const { id, content, author } = props.comments;
return (
<div>
<p>{id}</p>
<p>{content}</p>
<p>{author}</p>
</div>
)
}
组件渲染优化
1.shouldComponentUpdate
假设上面的例子我们用setInterval替换setTimeout做轮询。Comment组件将会一直渲染,这时候我们用shouldComponentUpdate函数来判断setState的值前后是否一致来决定是否渲染。
shouldComponentUpdate()包含两个参数。nextProps, nextState。
用即将渲染的数据跟现在的数据作比较。如果一致返回false 不渲染组件,不一致返回true 渲染组件
class Comment extends Component {
shouldComponentUpdate (nextProps, nextState) {
//性能优化
if (nextProps.comment.content === this.props.comment.content) {
return false
} else {
return true
}
}
render () {
console.log('render');
const { id, content, author } = this.props.comment;
console.log(content);
return (
<div>
<p>{id}</p>
<p>{content}</p>
<p>{author}</p>
</div>
)
}
}
2.PureComponent
PureComponent 浅比较 比较的是值。当值不变的时候,组件不会重新渲染。但是上面的要传的是一个数组里面包含对象。这个PureComponent是无法识别的,会一直不断的渲染
import React, { Component,PureComponent } from 'react'
class Comment extends PureComponent{
render(){
console.log('render');
const { id, content, author} = this.props.comment;
return(
<div>
<p>{ id }</p>
<p>{ content }</p>
<p>{ author }</p>
</div>
)
}
}
这样写会一直渲染。想让组件不重复渲染,需要在聪明式组件传值的时候,将传的值控制为字符串、布尔值等
<Comment key={item.id} id={item.id} author={item.author} content={item.content}></Comment>
再将接收值修改一下
const { id, content, author} = this.props;
但是要传的值一旦多了,这个方法会写的很麻烦。所以可以用解构赋值的方式传值(重要)
<Comment key={item.id} {...item}></Comment>
3.React.memo()
一种高阶组件用法 React.memo()其实跟PureComponent用法差不多,里面传一个函数作为参数
const Comment = React.memo(({id, content, author})=>{
console.log('render');
return(
<div>
<p>{ id }</p>
<p>{ content }</p>
<p>{ author }</p>
</div>
)
})
Comment为高阶组件
组件组合
React官方文档有这样一句话:推荐是用组件组合而非继承实现代码重用
这个跟Vue中的插槽用法一样:定义一个函数组件,用props.children作为匿名插槽。其他组件调用该组件,在该组件标签内容内编写要要展示的数据。如下例子
function Dialog(props){
return(
<div>
{/* 匿名插槽 */}
{ props.children }
</div>
)
}
function WelcomeDialog(){
return(
<Dialog>
{/* 这些插到Dialoga组件中的props.children */}
<h3>Hello</h3>
<p>Welcome组件组合</p>
</Dialog>
)
}
export default class QRCodeMapping extends Component {
render() {
return (
<div>
<WelcomeDialog></WelcomeDialog>
</div>
)
}
}
类似于具名插槽功能
在父组件中的子组件标签内(这里是Dialog)添加标签的属性进行传值。在子组件内使用、渲染。要传的标签属性可以是一个jsx
function Dialog(props){
return(
<div style={{border:`3px solid ${props.color}`}}>
{/* 匿名插槽 */}
{ props.children }
<div>
{/* 具名插槽 */}
{props.btn}
</div>
</div>
)
}
function WelcomeDialog(){
const Welcomebtn = <Button type='primary'> 欢迎 </Button>
return(
// 这里在标签属性里传了值、组件
<Dialog color={'red'} btn={Welcomebtn}>
{/* 这些插到Dialoga组件中的props.children */}
<h3>Hello</h3>
<p>Welcome组件组合</p>
</Dialog>
)
}
高阶组件
组件设计的目的:保证组件功能的单一性。
高阶组件不是组件,本质上是一个函数,这个函数接受一个或多个组件,返回一个新组件,则当前组件为高阶组件.
- 创建组建高阶组件的函数,在这个函数对组件做处理(传值等操作)
- 编写要传的组件,可以用this.props来获取高阶组件函数传的值
- 调用高阶组件函数,并且将要传的组件作为参数传递
属性代理
// 本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新组件
const highOrderCom = (Comp) => {
//返回一个新组件
const NewComponent = (props) => {
// 属性代理
const attr = { type:'高阶组件' }
return <Comp {...props} {...attr}></Comp>
}
return NewComponent;
}
class Hoc extends Component {
render() {
return (
<div>
<h3>{this.props.type}</h3>
</div>
)
}
}
export default highOrderCom(Hoc);
重写组件
const highOrderCom = (Comp) => {
//返回一个新组件
return class extends Component{
constructor(props){
super(props);
}
componentDidMount(){
console.log('发起fetch请求');
}
render(){
return(
<Comp {...this.props} type='高阶组件'></Comp>
)
}
}
}
高阶函数 定义:接收的参数是函数或者返回值是函数
- 常见的:数组的遍历的相关方法、定时器、Promise、高阶组件
- 作用:实现一个更加强大的动态功能
- 高阶组件设计的思想也是如此,传入一个组件,返回一个功能更强大的组件
理解高阶组件
-
我们为什么需要高阶组件?
- react高阶组件能够让哦我们写出易于维护的react代码
-
高阶组件是什么
-
本质上是一个函数,这个函数接收一个组件或者多个组件,返回一个新组件
-
比如:给一个赛亚人,返回一个超级赛亚人
-
y = kx + b
-
x好比是普通组件,k和b就是当前普通组件定制的属性和方法,y就是返回的新组件
-
-
如何实现高阶组件
- 属性代理是最常见的实现方式。
-
好处:常用的方法独立出来,多次复用。
- 反向继承
-
高阶组件的应用
- 页面复用
- 权限控制
- 打印日志
链式调用与装饰器
写一个打印日志的高阶组件
const withLog = (Comp) => {
console.log(Comp.name + '渲染了');
return (props) => {
return <Comp {...props}></Comp>
}
}
链式调用 由内到外去执行
export default highOrderCom(withLog(Hoc));
装饰器
下载:
npm install --save-dev babel-plugin-transform-decorators-legacy @babel/plugin-proposal-decorators
配置好后可以这么写
@highOrderCom
@withLog
class Hoc extends Component{....}
等同于highOrderCom(withLog(Hoc))
组件通信Context
Context提供了一个无需为每层组件手动添加props就能在组件树间进行数据传递的方法。
Context设计的⽬的是为了共享那些全局的数据.
Context == vue中的provide和inject react中的provider和consumer
使用Context
React.createContext()创建一个上下文对象 例:
const ThemeContext = React.createContext();
- 在要传递数据的组件中用
ThemeContext.Provider标签包裹内容,并且在标签属性中定义要传的值。 例:
<ThemeContext.Provider value={this.state.store}>
<Toolbar></Toolbar>
</ThemeContext.Provider>
-
在需要用到
Context对象的组件中使用 两种方法- 定制当前的创建的上下文对象 为当前实例的静态属性.
static contextType = ThemeContext然后在渲染的方法中使用
this.context获取共享的数据
完整代码示例
import React, { Component } from 'react'
import { Button } from 'antd'
const ThemeContext = React.createContext();
class ThemeBtn extends Component{
constructor(props){
super(props);
console.log(this);
}
//定制创建的上下文对象为静态属性
static contextType = ThemeContext
render(){
return(
//this.context来获取context里的数据
<Button type={this.context.type}>{this.context.name}</Button>
)
}
}
function Toolbar(props){
return <ThemeBtn></ThemeBtn>
}
export default class ContextSimple extends Component {
constructor(props){
super(props);
this.state={
store:{
type:'primary',
name:'按钮'
}
}
}
render() {
return (
// 给上下文对象传值
<ThemeContext.Provider value={this.state.store}>
<Toolbar></Toolbar>
</ThemeContext.Provider>
)
}
}
- 基于函数渲染
在ThemeBtn 组件中的渲染方法里使用
ThemeContext.Consumer标签,内容为一个插值{}里面包含着一个箭头函数,传的值为定义的值,然后直接使用
class ThemeBtn extends Component{
constructor(props){
super(props);
console.log(this);
}
render(){
return(
<ThemeContext.Consumer>
{
//2. 建议使用这种方式 更好理解 基于函数渲染
(value)=>{
return <Button type={value.type}>{value.name}</Button>
}
//另外一种写法
//value=><Button type={value.type}>{value.name}</Button>
}
</ThemeContext.Consumer>
)
}
}