ReactJS入門 - 各階層Component 溝通 - 筆記長也NotesHazuya

ReactJS入門 - 各階層Component 溝通

2021-07-27 14:03:00   ReactJS

本篇將會介紹各個 Component 在互相傳送資料以及呼叫函式的方式,雖然已經有更好的解決方法(例如 useContext),但是瞭解各個層級的 Component 之間的溝通還是足以應付簡單的情況。

幾種狀況

元件之間的溝通狀況大概可以分為這幾種:

  1. 子對父
  2. 父對子
  3. 子對子(同父元件)
  4. 某個父元件對某個子元件

本篇會使用以下架構說明:

index.js
 |_ App.js
   |_ A.js
     |_ B.js
     |_ C.js

並且以 funtional component 來讓範例更容易閱讀。

單向資料流

先說前提,由於 react 只允許單向資料流,因此所有資料只能由父元件傳向子元件。

子對父

子元件取得父元件資料與函式

就是使用 props,可以參考這一篇

子元件修改父元件綁定的 props

如開頭所述,不能直接用 props.count = 100  來修改,具體作法是綁定一個父元件函式在子元件上,然後讓子元件呼叫修改數值。

A.js

import React, { useState } from 'react';
import B from './B';

const A = (props)=>{
    const [count, setCount] = useState(0);
    const plusplus = () => {
        setCount(count+1);
        console.log(count);
    }
    return(
        <>
            <B count={count} handlePlus={plusplus}></B>
        </>
    )
}
export default A;

B.js

import React from 'react';

const B = (props)=>{
    return(
        <>
        <p>我是B,我在計算東西,現在={props.count}</p>
        <button onClick={props.handlePlus}>++</button>
        </>
    )
}
export default B;

今天 A 傳給 B 一個名為 count 的資料,而我們要藉由 A 當中的 plusplus 方法來讓 B 來改變 count 的值。

父對子

父元件呼叫子元件函式

父元件無法直接呼叫子元件的函式。作法是綁一個 state 到子元件上面,然後在父元件更新綁定的 state,並使用 useEffect 在 B 元件偵測 state 的更動來呼叫函式。

A.js

import React, { useState } from 'react';
import B from './B';

const A = (props)=>{
    const [count, setCount] = useState(0);
    const plusplus = () => {
        setCount(count+1);
    }
    return(
        <>
            <B count={count} handlePlus={plusplus}></B>
        </>
    )
}
export default A;

B.js

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

const B = (props)=>{
    const runB = () => {
        setMsg("Good!");
    }
    const [msg, setMsg] = useState("");
    useEffect(
        () => {
            if (props.count >= 10) {
                runB();
            }
            
        },[props.count]
    );
    return(
        <>
        <p>我是B,我在計算東西,現在={props.count}</p>
        <button onClick={props.handlePlus}>++</button>
        <p>{msg}</p>
        </>
    )
}
export default B;

這裡繼續接續上面的部分,當 count 的數值大於 10 的時候就執行 B 當中的 runB()。

父元件取得子元件資料

父元件也無法取得子元件資料,作法是由父元件綁定一個 state 以及一個接收子元件的數值函式在子元件上,然後由父元件更動綁在子元件的 state 來觸發接收數值的函式,把資料傳入該函式之中。

A.js

import React, { useState } from 'react';
import B from './B';

const A = (props)=>{
    const [count, setCount] = useState(0);
    const [BValue, setBValue] = useState("");
    const getBValue = (value) => {
        setBValue(value);
    }
    const plusplus = () => {
        setCount(count+1);
    }
    return(
        <>
            <B count={count} handlePlus={plusplus} getB={getBValue}></B>
            <p>{BValue}</p>
        </>
    )
}
export default A;

B.js

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

const B = (props)=>{
    const runB = () => {
        setMsg("Good!");
    }
    const [msg, setMsg] = useState("");
    useEffect(
        () => {
            if (props.count >= 10) {
                runB();
            }
            if (props.count >= 20) {
                props.getB("OHOHOH!");
            }
        },[props.count]
    );
    return(
        <>
        <p>我是B,我在計算東西,現在={props.count}</p>
        <button onClick={props.handlePlus}>++</button>
        <p>{msg}</p>
        </>
    )
}
export default B;

一樣接續前面,當 count 大於 20 ,就回傳 "OHOHOH!" 字串給 A。

子對子

同一父元件A,子元件B修改子元件C的資料

簡單來說,就是要透過 A 這個父元件綁定一個 state 到 C 元件,然後綁定一個函式到 B 上面,然後由 B 呼叫函式來改變 C 上面的 props。

A.js

import React, { useState } from 'react';
import B from './B';
import C from './C';

const A = (props)=>{
    const [count, setCount] = useState(0);
    const plusplus = () => {
        setCount(count+1);
    }
    return(
        <>
            <B handlePlus={plusplus}></B>
            <C count={count}></C>
        </>
    )
}
export default A;

B.js

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

const B = (props)=>{

    return(
        <>
        <p>我是B</p>
        <button onClick={props.handlePlus}>++</button>
        </>
    )
}
export default B;

C.js

import React from 'react';

const C = (props)=>{
    return(<p>我是C,count 的值是:{props.count}</p>)
}
export default C;

由 B 先呼叫在 A 的函式,而 A 的函式會更新綁在 C 上面的 props。

 

同一父元件A,子元件B呼叫子元件C的函式

一樣先在 A 綁定一個 state 在 C 上面,然後在 A 宣告一個改變 state 的函式並將其綁到 B 上面。

A.js

import React, { useState } from 'react';
import B from './B';
import C from './C';

const A = (props)=>{
    const [count, setCount] = useState(0);
    const plusplus = () => {
        setCount(count+1);
    }
    return(
        <>
            <B handlePlus={plusplus}></B>
            <C count={count}></C>
        </>
    )
}
export default A;

B.js

import React from 'react';

const B = (props)=>{

    return(
        <>
        <p>我是B</p>
        <button onClick={props.handlePlus}>++</button>
        </>
    )
}
export default B;

C.js

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

const C = (props)=>{

    const [count20,setCount20] = useState("");
    const goodJob = () => {
        setCount20("goodJob");
    }
    useEffect(
        () => {
            if (props.count >= 20) {
                goodJob();
            }
        }, [props.count]
    )

    return(<p>我是C,count 的值是:{props.count}<br/>{count20}</p>)
}
export default C;

先由 B 去設定 count,然後當 count >= 20 的時候就執行 C 的goodJob函數。

同一父元件A,子元件B取得子元件C的資料

在父元件分別綁定 state 到兩個子元件,並分別宣告一個觸發改變 state 的函式以及一個接收來自 C 值的函式,也分別綁在子元件上面。

A.js

import React, { useState } from 'react';
import B from './B';
import C from './C';

const A = (props)=>{
    const [Cdata, setCdata] = useState("");
    const [count, setCount] = useState(0);
    const getCdata = (value) => {
        setCdata(value);
    }
    const handleCount = () => {
        setCount(count+1);
    }
    return(
        <>
            <B handleCount={handleCount} count={count} Cdata={Cdata}></B>
            <C getCdata={getCdata} count={count}></C>
        </>
    )
}
export default A;

B.js

import React from 'react';

const B = (props)=>{

    return(
        <>
        <p>我是B</p>
        <button onClick={props.handleCount}>取得C</button>
        <p>{props.Cdata}</p>
        </>
    )
}
export default B;

C.js

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

const C = (props)=>{

    const [count20,setCount20] = useState("Good");
    useEffect(
        () => {
            if (props.count >= 1) {
                props.getCdata(count20);
            }
        }, [props.count]
    )
    return(<p>我是C</p>)
}
export default C;

當 B 呼叫 handleCount 時會觸發改變 C 的 props,而 C 的 props 改變會觸發 getCdata 將 C 的值丟回去給 A 的 Cdata state,這時候 Cdata 也被綁在 B 上面 B 就會拿到 C 的值。

根元件對某子元件

如果改成以下架構

   A.js
     |_ B.js
       |_ C.js

當 A 要傳給 C 的時候 B 就要被作為中間站,如果要一直傳下去那就會很麻煩,每一層都要傳 props,即便中間的元件根本用不到這個資料。

把根元件綁在 B 元件上的 props 全部綁給 C

可以直接利用 spread  operator 的方式來分配

A.js

import React from 'react';
import B from './B';

const A = (props)=>{

    return(
        <>
            <B toC={"abc"} toC2={"ab3"}></B>
        </>
    )
}
export default A;

B.js

import React from 'react';
import C from './C'
const B = (props)=>{
    return(
        <>
        <C {...props}></C>
        </>
    )
}
export default B;

C.js

import React from 'react';

const C = (props)=>{

    return(<p>我是C,我的Props={props.toC + props.toC2}</p>)
}
export default C;

不綁定所有 props

在第一個中繼元件當中篩寫函式,使用 [ ] 物件的方式來取得 props,並綁到每一個中繼元件以及目標元件,以下範例中繼是 B ,而目標則是 C。

A.js

import React from 'react';
import B from './B';

const A = (props)=>{

    return(
        <>
            <B toC={"abc"} toC2={"ab3"}></B>
        </>
    )
}
export default A;

B.js

import React from 'react';
import C from './C'
const B = (props)=>{
    const handleSendData = (name) => {
        return props[name];
    }
    const handleSendFunc = (method, ...arg) => {
        return props[method](...arg);
    }
    return(
        <>
        <C handleSendData={handleSendData} handleSendFunc={handleSendFunc}></C>
        </>
    )
}
export default B;

C.js

import React from 'react';

const C = (props)=>{

    return(<p>我是C,我的Props={props.handleSendData("toC") + props.handleSendData("toC2")}</p>)
}
export default C;

useContext

useContext 可以更容易的共用 props,可以直接參考這篇文章

關於作者


長也

資管菸酒生,嘗試成為網頁全端工程師(laravel / React),技能樹成長中,閒暇之餘就寫一些筆記。 喔對了,也愛追一些劇,例如火神跟遺物整理師,推推。最愛的樂團應該是告五人吧(?)