React組件復用方式


Component

React核心思想,一切皆是組件,通過組件可以達到最理想的代碼復用

栗子

import React from 'react';
export default class ContainerComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.handlePlus = this.handlePlus.bind(this);
    }

    handlePlus() {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>{`I am a container component.`}</h1>
            <p>{`當前計數:${this.state.count}`}</p>
            <button onClick={this.handlePlus}>plus</button>
        </div>
    }
}

很簡單的一個組件,如果其他需要用,直接引用即可

import ContainerComponent from './ContainerComponent';

但是為了滿足最佳實踐,我們將該組件分成容器組件跟展示組件

容器組件:

import React from 'react';
import ShowComponent from './ShowComponent';
export default class ContainerComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.handlePlus = this.handlePlus.bind(this);
    }

    handlePlus() {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>{`I am a container component.`}</h1>
            <ShowComponent
                count={this.state.count}
                handlePlus={this.handlePlus}
            />
        </div>
    }
}

展示組件

export default ShowComponent;

function ShowComponent(props) {
    return <div>
        <p>{`當前計數:${props.count}`}</p>
        <button onClick={props.handlePlus}>plus</button>
    </div>
}

這里啥是容器組件,啥是展示組件?

容器組件:簡單理解就是含有處理邏輯,處理數據的容器

展示組件:只關心展示,跟數據無關,像控件,無狀態組件更貼切。

現在需求變了,如果展示組件不僅限於ShowComponent,現在多了ShowComponent2,showComponent3等等,怎么處理,噢?首先想到了什么?沒錯就是this.prop.children,

我們嘗試下。

children

栗子

ContainerComponent

import React from 'react';
export default class ContainerComponent extends React.Component {
    constructor(props) {
        super(props)
    }

    render() {
        return <div>
            <h1>{`I am a container component.`}</h1>
            {this.props.children}
        </div>
    }
}

ShowComponent、ShowComponent2、ShowComponent3等等

export default ShowComponent;

function ShowComponent(props) {
    return <div>
        <p>{`當前計數:${props.count}`}</p>
        <button onClick={props.handlePlus}>plus</button>
    </div>
}

class ShowComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
        this.handlePlus = this.handlePlus.bind(this);
    }

    handlePlus() {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <p>{`當前計數:${this.state.count}`}</p>
            <button onClick={this.handlePlus}>plus</button>
        </div>
    }
}

可以實現我們的目的,但是缺點也比較明顯,就是沒辦法將容器組件與展示組件分開,如果想分開,我們需要在ShowComponent中進行分,其實不難看出,this.props.children更像是一種layout的解決方案,僅用來定制layout,具體邏輯放到每個具體Component中進行。那么怎么處理這種情況更好呢~我們來看看一個解決方案—>回調渲染。

回調渲染

什么是回調渲染呢,簡單來講,就是采用調用的形式使用children API,就是this.props.children(),看個例子

栗子

import React from "react";
import ReactDOM from "react-dom";
import ContainerComponent from "./ContainerComponent";
import ShowComponent from "./ShowComponent";

{
  ReactDOM.render(
    <ContainerComponent>
      {({ count, handlePlus }) => (
        <ShowComponent count={count} handlePlus={handlePlus} />
      )}
    </ContainerComponent>,
    document.getElementById("app")
  );
}

ContainerComponent

export default class ContainerComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.handlePlus = this.handlePlus.bind(this);
    }

    handlePlus() {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>{`I am a container component.`}</h1>
            {
                this.props.children(
                    {
                        count: this.state.count,
                        handlePlus: this.handlePlus
                    }
                )
            }
        </div>
    }
}

ShowComponent

export default ShowComponent;

function ShowComponent(props) {
    return <div>
        <p>{`當前計數:${props.count}`}</p>
        <button onClick={props.handlePlus}>plus</button>
    </div>
}

既可以達到ShowComponent與ContainerComponent解耦的目的(這里的意思是可以隨意更改ShowComponet,可以換成其他組件),又可以實現了展示組件與容器組件分離。這只是個demo

那么回調渲染到底解決了什么問題呢~回調渲染可以實現外層組件與內層組件的數據傳遞,可以很好的分離組件以及邏輯復用。

談到回調渲染,我們就會聯想到另一個方案,Render Props,那么什么是Render Props呢

Render Props

RenderProps我認為是回調渲染的一種衍生方式,利用一個函數,進行組件傳遞,這個組件就是可變的部分,其余部分實現代碼復用。我們試着用RenderProps方式對上個栗子進行改造,我們看個栗子。

栗子

import ContainerComponent from "./ContainerComponent";
import ShowComponent from "./ShowComponent";

{
  ReactDOM.render(
    <ContainerComponent
      render={({ count, handlePlus }) => (
        <ShowComponent count={count} handlePlus={handlePlus} />
      )}
    />,
    document.getElementById("app")
  );
}

ContainerComponent

import React from 'react';
import ShowComponent from './ShowComponent';
export default class ContainerComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.handlePlus = this.handlePlus.bind(this);
    }

    handlePlus() {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>{`I am a container component.`}</h1>
            {
                this.props.render({
                    count: this.state.count,
                    handlePlus: this.handlePlus
                })
            }
        </div>
    }
}

ShowComponent

export default ShowComponent;

function ShowComponent(props) {
    return <div>
        <p>{`當前計數:${props.count}`}</p>
        <button onClick={props.handlePlus}>plus</button>
    </div>
}

其實跟回調渲染大同小異,回調渲染用的是React原生方法(this.props.children),RenderProps更靈活一些,可以定義多個函數,隨意傳遞,比如

 ReactDOM.render(
    <ContainerComponent
      render={({ count, handlePlus }) => (
        <ShowComponent count={count} handlePlus={handlePlus} />
      )}
      renderMsg={() => <div>我是msg</div>}
      renderColor={() => <div>我是color</div>}
    />,
    document.getElementById("app")
  );

好的總結一下,RenderProps解決了什么問題?

同樣達到了代碼復用,組件拆解,降低耦合性,同時具備了回調渲染沒有的靈活性。

Hook

在解決上面demo的例子,hook只是換了一種代碼展示形式,解決方法是沒變的,我們看改完的栗子

import ContainerComponent from "./ContainerComponent";
import ShowComponent from "./ShowComponent";

{
  ReactDOM.render(
    <ContainerComponent
      render={({ count, handlePlus }) => (
        <ShowComponent count={count} handlePlus={handlePlus} />
      )}
    />,
    document.getElementById("app")
  );
}

ContainerComponent

import React, { useState, useEffect } from "react";
import ShowComponent from './ShowComponent';
function ContainerComponent(props) {
    const [count, setCount] = useState(0);
    const handlePlus = () => {
        setCount(count + 1);
    }
    return <div>
        <h1>{`I am a container component.`}</h1>
        {
            props.render({
                count: count,
                handlePlus: handlePlus
            })
        }
    </div>
}


export default ContainerComponent;

ShowComponent

import React, { useState, useEffect } from "react";


function ShowComponent(props) {
    return <div>
        <p>{`當前計數:${props.count}`}</p>
        <button onClick={props.handlePlus}>plus</button>
    </div>
}

export default ShowComponent;

依然利用了RenderProps,只是將Class組件變成了Hook組件,這個其實體現不出來Hook的要解決的問題,

Hook解決的問題跟HOC是比較類似的,側重點是邏輯復用。看個栗子。

公用部分

function useFetch() {
  const [data, setData] = useState();
  useEffect(() => {
    new Promise((resolve) => {
      resolve({
        name: "dqhan",
        age: 28,
      });
    }).then((res) => {
      setData(res);
    });
  }, []);

  return data;
}

組件App

function App() {
  let data = useFetch();
  return (
    <div>
      <div>App1</div>
    </div>
  );
}

組件App2

function App2() {
  let data = useFetch();
  return (
    <div>
      <div>App2</div>
    </div>
  );
}

提取組件加載完成時獲取數據的復用邏輯,這樣能讓代碼復用看起來更直觀。當然還有最后一種方式實現代碼復用,就是HOC。這里就不談了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM