react入门之react组件化

react 组件化

image-20221211213057132

Ant-Design

yarn add antd -S

基础使用:

import React, { Component } from 'react'
import { Button } from 'antd'
import 'antd/dist/reset.css'

export default class Ant extends Component {
    render() {
        return (
            <div>
                <Button type="primary">123</Button>
            </div>
        )
    }
}

效果:

image-20221213142558126

高级设置

按需引入

聪明组件和傻瓜组件

也可以认为是容器组件和展示组件

组件化设计的优点:

  • 逻辑和内容分离
  • 重用性高
  • 复用性高
  • 容易测试

:::tip

类型需要对应起来

 this.state = {
    comments: [],
 }
this.setState({
           comments: [
                  {
                        id: 1,
                        author: 'facebook',
                        content: 'react非常好',
                   },
                   {
                        id: 2,
                        author: '尤雨溪',
                        content: '我觉得vue更好',
                   },
           ],
})

:::

组件的使用和傻瓜组件及聪明组件的使用:

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

export default class CommentList extends Component {
    constructor(props) {
        super(props)
        this.state = {
            comments: [],
        }
    }
    componentDidMount() {
        setTimeout(() => {
            this.setState({
                comments: [
                    {
                        id: 1,
                        author: 'facebook',
                        content: 'react非常好',
                    },
                    {
                        id: 2,
                        author: '尤雨溪',
                        content: '我觉得vue更好',
                    },
                ],
            })
        }, 1000)
    }
    render() {
        return (
            <div>
                {this.state.comments.map((item, index) => {
                    return (
                        <Comment key={index} comment={item.content}></Comment>
                    )
                })}
            </div>
        )
    }
}
import React, { Component } from 'react'

export default class Comment extends Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>{this.props.comment}</div>
    }
}

上面的 Comment 就是一个傻瓜式组件

而 CommentList 则是一个聪明式组件

前者只负责展示 -> 按道理只需要使用 function 组件就行

而后者需要改变 state ,所以最好使用 类组件

组件渲染的优化方案

react 并不会帮你判断新的内容和旧的内容是不是一样的,所以我们需要手动帮助 react 来优化性能。

比如使用shouldComponentUpdate来帮助react判断是否需要更新。

此外,我们可以使用 PureComponent ,但注意一个问题,PureComponent使用的是浅比较,无法比较数组或者对象之类。

解决方法:

image-20221213151307571

字段多的时候:

image-20221213151354370

高阶组件的方法:

image-20221213151701376

组件组合而非继承

react : 我们推荐使用组件的组合来实现组件的重用

import { Button } from 'antd'
import React, { Component } from 'react'

function Dialog(props) {
    // 匿名插槽
    return (
        <div style={{ border: `3px solid ${props.color || 'red'}` }}>
            {props.children}
            {props.btn}
        </div>
    )
}

function WelcomeDialog() {
    const WelBtn = <Button type="primary">欢迎!</Button>
    return (
        <Dialog color="blue" btn={WelBtn}>
            <h3>welcome</h3>
            <p>ljj</p>
        </Dialog>
    )
}
export default class Compond extends Component {
    render() {
        return (
            <div>
                <WelcomeDialog />
            </div>
        )
    }
}

WelcomeDialog 就是整合了 Dialog 从而实现了组件的复用。

效果:

image-20221213153303517

我们可以在使用到 Dialog 的地方一直重复使用,从而达到优化代码的目的。

HOC高阶组件

高阶组件 -> 高阶函数:接受一个组件或者多个组件,返回一个组件

组件设计的目的:保证组件功能上的单一性了

高阶组件是为了解决代码的重用性!

y = kx + b
// y 就是高阶组件 x 就是普通组件

案例1:

image-20221213165735609

案例2:

import React, { Component } from 'react'

class Hoc extends Component {
    render() {
        return (
            <div>
                <h3>
                    {this.props.type}
                    {this.props.name}
                </h3>
            </div>
        )
    }
}

// 高阶组件
const highOC = (Comp) => {
    const NewCom = (props) => {
        const attr = { type: '高阶组件' }
        return <Comp {...props} {...attr}></Comp>
    }
    return NewCom
}

export default highOC(Hoc)

案例3:

image-20221213171532759

与此相关的有高阶函数:

就是传入的是一个函数或者返回的是一个函数

本质上高阶组件就是一个高阶函数

详细讲解高阶组件

关于高阶组件的讲解:

  1. 我们为什么需要使用高阶组件?

    react 使用高阶组件能够帮助我们写出易于维护、高复用性的代码

  2. 高阶组件到底是什么?

    传入一个组件,返回一个组件(高级)

  3. 如何实现高阶组件?

    方法一:属性代理

image-20221213173014181

​ 使用属性代理的好处: 将常用的方法独立出来并且复用

​ 方法二:反向继承

  1. 高阶组件的应用
    • 代码复用
    • 权限的控制
    • 打印日志 -> 在每一个方法调用的时候添加日志

高阶组件 — 打印日志

只要某个组件需要打印公共的日志,就可以使用此组件。

import React, { Component } from 'react'

class Hoc extends Component {
    render() {
        return (
            <div>
                <h3>
                    {this.props.type}
                    {this.props.name}
                </h3>
            </div>
        )
    }
}

// 高阶组件
const highOC = (Comp) => {
    const NewCom = (props) => {
        const attr = { type: '高阶组件' }
        return <Comp {...props} {...attr}></Comp>
    }
    return NewCom
}

// 打印日志的高阶组件
const withLog = (Comp) => {
    console.log(Comp.name + '渲染了')
    const NewHoc = (props) => {
        return <Comp {...props}></Comp>
    }
    return NewHoc
}

// 链式调用
export default highOC(withLog(withLog(Hoc)))

结果:

image-20221213174020504

装饰器处理链式调用

image-20221213180157172

使用 vite 创建的项目可以进行如下配置达到使用装饰器的效果:

第一步:

pnpm add @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D

第二步:

vite.config.js中加入:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          ["@babel/plugin-proposal-decorators", { legacy: true }],
          ["@babel/plugin-proposal-class-properties", { loose: true }],
        ],
      },
    }),
  ],
});

第三步:

jsconfig.json中加入:

{
  "compilerOptions": {
    "experimentalDecorators": true,
  }
}

然后即可使用装饰器,ts 中同理。

高阶组件 — 页面复用

案例:

MovieA:

import React, { Component } from 'react'
import { withFetch } from '../HOC/WithFetch'
import MovieList from './MovieList'

class MovieA extends Component {
    render() {
        return (
            <div>
                <MovieList movies={this.props.data}></MovieList>
            </div>
        )
    }
}

export default withFetch('A')(MovieA)

MovieB:

import React, { Component } from 'react'
import { withFetch } from '../HOC/WithFetch'
import MovieList from './MovieList'

class MovieB extends Component {
    render() {
        return (
            <div>
                <MovieList movies="this.props.data"></MovieList>
            </div>
        )
    }
}

export default withFetch('B'(MovieB))

MovieList:

import React, { Component } from 'react'

export default class MovieList extends Component {
    render() {
        return (
            <div>
                <ul>
                    {this.props.movies.map((item, index) => {
                        return <li key={index}>item</li>
                    })}
                </ul>
            </div>
        )
    }
}

高阶组价 — 权限控制

案例:

PageA:

import React, { Component } from 'react'
import { withAdminAuth } from '../HOC/withAdminAuth'

@withAdminAuth
class PageA extends Component {
    render() {
        return <div>PageA</div>
    }
}

export default PageA

PageB:

import React, { Component } from 'react'

export default class PageB extends Component {
    render() {
        return <div>PageB</div>
    }
}

withAdminAuth:

import React, { Component } from 'react'

export const withAdminAuth = (Comp) => {
    return class extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                isAdmin: true,
            }
        }
        componentDidMount() {
            // 从后端获取用户的权限
            const currentRole = 'Admin'
            this.setState({
                isAdmin: currentRole === 'Admin',
            })
        }
        render() {
            if (this.props.isAdmin) {
                return <Comp {...this.props}></Comp>
            } else {
                return <div>您不是管理员~</div>
            }
        }
    }
}

结果:

image-20221215182915395

组件通信Context

之前我们学习过父子之间的通信,可以复习一下。

但是这样传递有一个缺陷:就是每次传递都需要依赖 props ,这样只能一层一层地传递。

而利用 Context 我们不需要为每一层组件手动添加 props ,就可以实现在组件之间传递数据。

Context 设计主要是为了 全局 的数据传递。 -> 相当于 vue 的 provide 和 inject

案例:原来的情况下我们将数据传递的话,是需要按照下面的方式传递的:

import React, { Component } from 'react'

function Toolbar(props) {
    return <TimeBtn name={props.name}></TimeBtn>
}

function TimeBtn(props) {
    return <div>{props.name}</div>
}

export default class LearningContext extends Component {
    render() {
        return <Toolbar name="react"></Toolbar>
    }
}

image-20221215184338379

但是我们可以使用 Context 直接使得 TimeBtn 获得我们想要传递的内容。

image-20221215185417949

1.函数式组件代码:

  • 使用 createContext()
  • 使用 {useContext}
import React, { Component, useContext } from 'react'
const ThemeContext = React.createContext()

function Toolbar(props) {
    return <TimeBtn name={props.name}></TimeBtn>
}

function TimeBtn(props) {
    const context = useContext(ThemeContext)
    console.log(context)
    return <div>{context}</div>
}

export default class LearningContext extends Component {
    render() {
        return (
            <ThemeContext.Provider value="情报">
                <Toolbar name="react"></Toolbar>
            </ThemeContext.Provider>
        )
    }
}

2.类式组件代码:

  • 使用 createContext()
  • 使用 static contextType =
import React, { Component } from "react";
const ThemeContext = React.createContext()

class TimeBtn extends Component {
  static contextType = ThemeContext;
  render() {
    const value = this.context;
    return <div>{value}</div>;
  }
}

3.使用 Consumer

  • 只使用createContext()
import React, { Component, useContext } from 'react'
const ThemeContext = React.createContext()

function Toolbar(props) {
    return <TimeBtn name={props.name}></TimeBtn>
}

function TimeBtn(props) {
    return (
        <ThemeContext.Consumer>
            {(value) => {
                return <div>{value}</div>
            }}
        </ThemeContext.Consumer>
    )
}

export default class LearningContext extends Component {
    render() {
        return (
            <ThemeContext.Provider value="情报">
                <Toolbar name="react"></Toolbar>
            </ThemeContext.Provider>
        )
    }
}

上面的三个结果都是:

image-20221216104217868

高阶组件抽离Context

代码:

import React, { Component } from 'react'
const ThemeContext = React.createContext()

// Provider
const withProvider = (Comp) => {
    return class extends Component {
        render() {
            return (
                <ThemeContext.Provider value="react">
                    <Comp></Comp>
                </ThemeContext.Provider>
            )
        }
    }
}

// Consumer
const withConsumer = (Comp) => {
    return class extends Component {
        render() {
            return (
                <ThemeContext.Consumer>
                    {(value) => {
                        return <Comp value={value}></Comp>
                    }}
                </ThemeContext.Consumer>
            )
        }
    }
}

export { withConsumer, withProvider }
import React, { Component, useContext } from 'react'
import { withConsumer, withProvider } from '../HOC/withContext'

@withConsumer
class TimeBtn extends Component {
    render() {
        return <div>123 {this.props.value}</div>
    }
}

@withProvider
class LearningContext extends Component {
    render() {
        return <TimeBtn></TimeBtn>
    }
}

export default LearningContext

效果:

image-20221216174516658

antd表单组件基础使用

高性能表单控件,自带数据域管理。包含数据录入、校验以及对应样式。

import React from 'react'
import { Button, Checkbox, Form, Input } from 'antd'
const Antdform = () => {
    const onFinish = (values) => {
        console.log('Success:', values)
    }
    const onFinishFailed = (errorInfo) => {
        console.log('Failed:', errorInfo)
    }
    return (
        <Form
            name="basic"
            labelCol={{
                span: 8,
            }}
            wrapperCol={{
                span: 16,
            }}
            initialValues={{
                remember: true,
            }}
            onFinish={onFinish}
            onFinishFailed={onFinishFailed}
            autoComplete="off"
        >
            <Form.Item
                label="Username"
                name="username"
                rules={[
                    {
                        required: true,
                        message: 'Please input your username!',
                    },
                ]}
            >
                <Input />
            </Form.Item>

            <Form.Item
                label="Password"
                name="password"
                rules={[
                    {
                        required: true,
                        message: 'Please input your password!',
                    },
                ]}
            >
                <Input.Password />
            </Form.Item>

            <Form.Item
                name="remember"
                valuePropName="checked"
                wrapperCol={{
                    offset: 8,
                    span: 16,
                }}
            >
                <Checkbox>Remember me</Checkbox>
            </Form.Item>

            <Form.Item
                wrapperCol={{
                    offset: 8,
                    span: 16,
                }}
            >
                <Button type="primary" htmlType="submit">
                    Submit
                </Button>
            </Form.Item>
        </Form>
    )
}
export default Antdform

image-20221217111435057

根据实际情况我们要使用高阶组件催表单进行封装,以防重复使用。

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

城南顾北

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值