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有點費事,有興趣直接複製跑一下就完了.

歡迎你們點贊,留言交流

相關文章
相關標籤/搜索