React 如何封装一个简单的组件

前言

转眼,从接触react到如今,有一年多的时间了. 从一开始cv,学习语法,到如今本身写功能组件,封装. 分享一些心得.java

1. Component

react中组件的开始.先分析下这个react

//生命周期的接口
    interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> { }
    class Component<P, S> {
    
        //构造函数
        constructor(props: Readonly<P>);
        constructor(props: P, context?: any);
        //咱们更新组件的setState
        setState<K extends keyof S>(
            state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
            callback?: () => void
        ): void;

        forceUpdate(callBack?: () => void): void;
        //组件的逻辑
        render(): ReactNode;
        readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>;
        //state对象
        state: Readonly<S>;
        context: any;
        //组件引用
        refs: {
            [key: string]: ReactInstance
        };
    }
复制代码

日常写组件,经常使用到的就4个es6

render state setState refsbash

有人会说,还有constructor呢.antd

实际上,我这个几乎没用到.app

constructor

早期的react,常规应该是这样的:函数

state的定义,函数的定义,都在constructor里面. 如今依然看到许多人这样写.其实也没啥毛病.oop

但我以为react,JSX,es6不错的地方.都让js更像编译语言,而不是脚本语言. 说直接点.很是像java之类的语言学习

做为一个写了几年java的人,我我的以为这样看起来舒服不少.哈哈.ui

这个标志也是继承,复写的意思.

正题:

其实无论什么语言, 开发设计都应该遵循六大设计原则:

单一职责原则(SRP):一个组件只作一件事

里氏替换原则(LSP): 继承

依赖倒转原则(DIP):多态

接口隔离原则(ISP):不要滥用接口(别继承没用的)

迪米特法则(LOD): 耦合

开闭原则(OCP):扩展性强(逻辑写好了别瞎JB改.要让人能扩展)

原理并不难,但真的要作好,仍是很须要技术的(废话).


最实用的:

单一职责原则 里氏替换原则 开闭原则

从我在交流群,还有看到的代码来讲,包括我本身. 这3个是最经常使用,却最容易被忽略.

不扯犊子了.

实例.写一个定时的内容切换

首先,想好一个需求(单一指责),尽可能不要太复杂.不要想太多.

一.分析需求.定义参数:

参数:

1.间隔时间

2.当前展现内容(单个,或者多个)

3.开始,暂停

回调:

每次切换的回调

定义:

/**
 * 定时切换
 */
class Test extends Component {
    state = {
 
    };

    render() {
        const { time, open, children } = this.props;
        return (
            <div>

            </div>
        );
    }
}

Test.propTypes = {
    // 间隔时间
    time: PropTypes.number,
    // 是否启动
    open: PropTypes.bool,
};

export default Test;
复制代码

二.展现内容

有人会问,为何展现内容的参数没定义.

这就涉及react中.children这个参数了.

也就是咱们封装的组件下,包含的组件,就会在这个children里面 debug看一下

因此展现内容,就不须要特地再去写一个参数了. 初始化

componentDidMount() {
        this.notifyContent();
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.props.children !== prevProps.children) {
            this.notifyContent();
        }
    }

    notifyContent = () => {
        const { children } = this.props;
        const content = Array.isArray(children) ? children : [children];
        this.setState({
            content,
        });
    };
复制代码

三.写定时循环

这里就用setTimeout递归写了.

componentDidMount() {

        this.loop();
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.props.open !== prevProps.open) {
            this.loop();
        }
    }
    loop = () => {
        const { time = 1000, open = false } = this.props;
        //若是没开就关闭
        if (!open) {
            return;
        }
        setTimeout(() => {
            const { content, index } = this.state;
            const newIndex = index + 1;
            this.setState({
                index: newIndex >= content.length ? 0 : newIndex
            });
            this.loop();
        }, time);
    };
复制代码

四.切换时的回调

把loop改造一下,中间添加change方法

loop = () => {
        const { time = 1000, open = false } = this.props;
        if (!open) {
            return;
        }
        setTimeout(() => {
            const { content, index } = this.state;
            this.change(index, content[index]);
            
            const newIndex = index + 1;
            this.setState({
                index: newIndex >= content.length ? 0 : newIndex
            });
            this.loop();
        }, time);
    };

    change = (index, content) => {
        const { onChange, change } = this.props;
        if (onChange) { //antd的form表单,默认会设置.
            onChange(index, content);
        }
        if (change) {//因此通常写2个.
            change(index, content);
        }
    };

复制代码

五.若是我想添加扩展?

getItem = (index) => {
        const { wrapper } = this.props;
        const item = this.state.content[index];
        if (wrapper) {
            return wrapper(item, index);
        }
        return item;
    };

复制代码

最终

import React, { Component } from 'react';
import PropTypes from 'prop-types';

/**
 * 定时切换
 */
class Test extends Component {
    state = {
        index: 0,
        content: [],
    };

    componentDidMount() {
        this.notifyContent();
        this.loop();
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.props.open !== prevProps.open) {
            this.loop();
        }
        if (this.props.children !== prevProps.children) {
            this.notifyContent();
        }
    }
    //更新,保存主内容引用
    notifyContent = () => {
        const { children } = this.props;
        const content = Array.isArray(children) ? children : [children];
        this.setState({
            content,
        });
    };
    // 定时循环
    loop = () => {
        const { time = 1000, open = false } = this.props;
        if (!open) {
            return;
        }
        setTimeout(() => {
            const { content, index } = this.state;
            this.change(index, content[index]);
            
            const newIndex = index + 1;
            this.setState({
                index: newIndex >= content.length ? 0 : newIndex
            });
            this.loop();
        }, time);
    };
    // 改变回调
    change = (index, content) => {
        const { onChange, change } = this.props;
        if (onChange) { //antd的form表单,默认会设置.
            onChange(index, content);
        }
        if (change) {//因此通常写2个.
            change(index, content);
        }
    };
    // 获取展现内容
    getItem = (index) => {
        const { wrapper } = this.props;
        const item = this.state.content[index];
        if (wrapper) {
            return wrapper(item, index);
        }
        return item;
    };

    render() {
        const { index } = this.state;
        return (
            <div>
                {this.getItem(index)}
            </div>
        );
    }
}

Test.propTypes = {
    time: PropTypes.number,
    open: PropTypes.bool,
    wrapper: PropTypes.func,
    change: PropTypes.func,
};

export default Test;
复制代码

使用

<Test
      open={true}
      time={5000}
      change={(i, item) => {
         console.log(i, item);
      }}>
      <p>1</p>
      <p>2</p>
      <p>3</p>
    </Test>
复制代码

结语

是否是很简单.

propTypes是个好东西,必定要用 (若是你想让的你代码好维护的话)

效果就不贴了,作gif有点费事,有兴趣直接复制跑一下就完了.

欢迎你们点赞,留言交流

相关文章
相关标签/搜索