在了解state怎麼定義、怎麼操作後,還有一個很重要的部分,就是state在元件中傳遞的方式。這個部分也就是大家普遍都很熟悉的props,不只在Vue中是使用props,React一樣也是使用props,雖然使用的方式和方法很類似,但其實還是有一些不太一樣的地方,今天就繼續從props學習吧!
今天一樣從Vue和React針對props這部分的使用方式開始!
想要在Vue的Template上使用props會需要使用v-bind語法(可以簡寫為 :
)將state放在需要透過props接收的子元件。子元件則需要透過defineProps來定義父元件透過props傳過來的state,才可以使用這個state。defineProps的動作除了可以定義props的type,還可以設定props的預設值,以及是不是required。另外,除了傳遞string、number、object、array等,如果有需要的話,還可以從props傳入函式。
父元件
<template>
<div>
<h2>Vue Props</h2>
// 透過v-bind綁定parentState
<childComponent :parentState="parentState" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import childComponent from './components/ChildComponent.vue';
const parentState = ref('This is a parent state.');
</script>
子元件
<template>
<div class="child">
<h3>childComponent section</h3>
// 把props用{{}}放入
<p>{{ parentState }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const content = ref('default content');
// 定義props
defineProps({
parentState: {
type: String,
}
});
</script>
這樣把父元件帶入子元件後,父元件的state就可以被放在子元件使用。
React的使用方式與Vue有些許不同,因為把props帶到子元件上的方式並不是透過Vue才有的v-bind,而是要透過 props名稱={state} 的方式把props帶上去,且被帶到子元件的props對子元件來說是子元件component function的參數,通常都會用解構的方式取得。另外,React的props也可以帶上函式,而針對函式這部分的使用情境上,我覺得也是和Vue有較大差異的地方,這部分後面再來看。
父元件
import './App.css';
import { useState } from 'react';
import ChildComponent from './components/ChildComponent';
function App() {
const [parentState, setParentState] = useState('This is a parent state.');
return (
<div className="App">
<h2>React props</h2>
// 把state透過props帶上
<ChildComponent parentState={parentState}/>
</div>
);
}
子元件
const ChildComponent = (props) => {
// react不需要做定義props的動作,但是需要從props把要使用的state取出來
const { parentState } = props;
return (
<div className="child">
<h3>childComponent section</h3>
<p>{ parentState }</p>
</div>
)
}
export default ChildComponent;
這樣也就能把父元件的state被放在子元件使用。
雖然React的props不需要額外進行「定義state」的動作,就可以從component function的參數取得,但其實還是可以額外進行定義state的預設值和type,以及設定isRequired的動作。但如果要使用propTypes來定義props的型別,需要額外安裝prop-types。
import PropTypes from "prop-types";
ChildComponent.defaultProps = {
parentState: 'default',
};
ChildComponent.propTypes = {
parentState: PropTypes.string,
requiredState: PropTypes.bool.isRequired,
};
當型別有錯,或是設定了isRequired,但是沒帶上這個props的話,就會在console跳出相對應的警告訊息。
在Vue的使用情境中,prop大多主要是拿來把父層的state向下傳遞給子層,但是React的使用情境中,props卻不只有傳遞state,也很常拿來傳遞變更父層state的函式,或是以render props的寫法來傳遞一些共用的邏輯或畫面,讓我們能更靈活地使用元件。其實這樣的使用方式跟我們原先理解和知道的「props可以傳遞一般的值或函式」的部分是一樣的,只是以實際應用來看的話,與Vue平常在使用state的情境有些差異,所以才想特別拿出來說,這也是我一開始碰React沒有馬上反應過來的部分。
這裡直接用一些更實際的案例來看「傳遞變更父層state的函式,或是以render props的模式來傳遞一些共用的邏輯或畫面」的部分。
為了保持單向資料流,以及讓component function維持為pure的函式,透過props傳遞到子元件的state都不能在子元件直接做改動,但是很多實際的實作情境中,還是會由子元件觸發父元件的state改動,這種時候就可以拿用props傳遞過來的state更新函式下去更新父元件的state。
例如這樣的使用情境:
下面是一個子元件,透過props拿到改動父元件內state的函式
const ChildComponent = ({ count, onAddCount }) => {
return (
<div className="container">
<h1>{count}</h1>
// 用父元件的state更新函式來更新父元件的state
<button onClick={onAddCount}>add</button>
</div>
);
};
export default ChildComponent;
props除了傳遞更新state的函式外,也可以透過render prop的寫法來取得保存於子元件內的共用邏輯來使用。
在這個情境下,子元件的設計方向會跟單純是一般元件的時候有點不太一樣,一般的子元件是都是單純return一些與畫面有關,拿來轉換成virtual DOM的內容。
例如:
import { useState } from "react";
export default function Button({children}) {
return (
<button>{children}</button>
)
如果是render props的寫法則除了會return與畫面顯示有關的內容外,回傳內容還會包含一個函式執行後的結果,而這個函式並不是子元件自己的函式,而是父元件透過props傳入的函式。
// 子元件
function Counter({ render }) {
// 將特定邏輯封裝在Counter元件裡面
const [count, setCount] = useState(10);
const addCount = () => {
setCount(count + 1);
};
return render(count, addCount);
}
export default Counter;
在實際上於父元件上使用的時候,傳一個函式給子元件使用。
import Counter from "./Counter";
export default function App() {
return (
<div>
<Counter
render={(count, addCount) => (
<div>
<p>Current count: {count}</p>
<button onClick={addCount}>Increment</button>
</div>
)}
/>
</div>
);
}
這樣也就能在父元件也使用到子元件的state或一些邏輯的操作。
這邊稍微反思一下render props這個寫法的思考模式會是什麼?
使用這個寫法的思考模式是「把邏輯和state保存在一個子元件中,在實際使用的時候,再透過用props傳入函式的方式,來取得子元件內的邏輯或state來使用
」。
看到這裡可能有些人會覺得這樣的操作似乎有點違反我們之前說過的單向資料流的概念,因為感覺前面的實作情境像是從父元件把子元件內的資料拿出來用或操作。但是這樣的寫法其實還是單向資料流的概念。雖然看起來像是父元件可以取得子元件的資料,但實際上的實作原理其實只是 我們把一個函式透過props傳入子元件,子元件再透過那個props來使用父元件的函式,並藉由那個函式來使用自己的state和函式
,所以雖然看起來像是從父元件拿到了子元件的state,但實際使用那個state的人,還是子元件自己。
今天看了一些關於props的使用,雖然Vue和React的props都可以用來傳遞一般的值和函式,但在React的使用情境中,props的用途更活,不只限於拿來傳遞state,這也是因為在寫JSX的時候,實際上就是在寫JavaScript,使得在整體的使用上,變化更多,寫法也更自由。而Vue的話,因為某些使用情境,已經有一些框架規定的用法可以使用,所以在一些使用情境上,例如從子元件要透過事件觸發父元件state的更新時,就有它自己的一套用法。但是Vue和React因為都是單向資料流,所以在傳遞props上,都一樣有掌握從父層往子層傳的原則,當然改動資料也依循這個原則。