React — Redux (Part 3)

Sittipong Saychum
React.js Thailand
Published in
4 min readJul 24, 2020

Part นี้จะพูดถึงการส่งผ่านค่าจาก component หนึ่ง ไปยังอีก component หนึ่ง และการเปลี่ยน component แบบที่ไม่ต้อง เปลี่ยนทั้งหน้าเพจ นอกจากนั้นผมจะกล่าวถึง Redux ซึ่งเป็น storage เก็บค่าต่างๆ ที่สามารถใช้ได้ทั้ง webapp ของเรา

ก่อนอื่นจะขอพูดถึง state ที่ใช้ในการเก็บค่าต่างๆ ภายใน component ซึ่งใน part 1 ผมใช้ class component แต่ใน part นี้ผมเปลี่ยนมาใช้ function component ที่มีวิธีเขียนต่างกัน

เรามาทำความเข้าใจ concept กันก่อน อย่าลืมว่า webapp ผมมี 3 ส่วน herder กับ footer อยู่เฉย ไม่มีการเปลี่ยนหน้าทั้ง webapp ส่วน content จะเป็น component ที่ เปลี่ยนไป

ใน content.js เนื่องจากเป็น function component ผมต้องเปลี่ยน constructor เพื่อ define state เป็นแบบนี้แทน

const [stOutput,setStOutput] = useState(false);
const [dataHook, setDataHook] = useState({
name: '',
email: '',
age: '',
gender: ''
});

จากโคดด้านบนผมมี state อยู่ 2 ตัว คือ stOutput เก็บค่า boolean ไว้ใช้สำหรับ ตอนเปลี่ยน component ตัวที่ 2 คือ dataHook ตัวนี้เป็น object เก็บค่า string name, email, age, gender

ใน form ที่ผมสร้างไว้จาก part ที่ 2 ผมเปลี่ยนเป็นแบบนี้

return (
<RowPanel>
{
stOutput ?
<ContentAfter data={dataHook}/>
:
<form onSubmit={submitHandler}>
<p>Enter your name:</p>
<input type="text" name="name" onChange={changeHandler}/>
<p>Enter your email:</p>
<input type="text" name="email" onChange={changeHandler}/>
<p>Enter your age:</p>
<input type="text" name="age" onChange={changeHandler}/>
<p>Select your gender:</p>
<input type="radio" name="gender" value="M" checked= { dataHook.gender === "M"} onChange={changeHandler} />Man
<input type="radio" name="gender" value="W" checked= {dataHook.gender === "W"} onChange={changeHandler} />Woman
<p></p>
<input type="submit" value="Submit" />
<p></p>
</form>
}
</RowPanel>
);

จากโคดด้านบน ค่า stOutput จะเป็นตัวเช็คว่า component นี้จะแสดงอะไร ระหว่าง form หรือ ไปเรียก ContentAfter component มาแสดงแทน

ค่าที่ใส่เข้าไปใน form จะถูก set ค่า state ที่ changeHandler และเมื่อกด submit submitHandler จะถูกเรียกมาทำงาน เรามาดู ทั้ง 2 function นี้ดีกว่า

function changeHandler(event){
setDataHook({
name: dataHook.name,
email: dataHook.email,
age: dataHook.age,
gender: dataHook.gender,
[event.target.name]: event.target.value
});
}
function submitHandler(event){
event.preventDefault();
setStOutput(true);
}

จากโคดจะเห็นว่า การ set ค่า object ของ dataHook จะต่างจาก class component ที่จำเป็น ต้อง set ค่าทุกตัวที่อยู่ใน object ไม่เช่นนั้นมันจะ set ค่าแค่ตัวเดียวแล้วตัวอื่นค่าจะเป็น “”

ใน submitHandler “event.preventDefault();” เป็นบรรทัดที่ทำให้การเปลี่ยน component ไม่ต้อง refresh ทั้งหน้าเพจ และทำการ setStOutput เป็น true เพื่อให้ Content component ไปเรียก ContentAfter component มาแสดงแทน

เราย้อนกลับไปที่ form จะเห็นบรรทัดนี้

<ContentAfter data={dataHook}/>

นี้คือการส่งข้อมูลที่เป็น object ไปยัง ContentAfter component ผมจะเขียนให้รับค่าแล้วนำมาแสดง

ContentAfter.js

function ContentAfter(props){
return (
<ColumPanel>
<h2>Listening Test</h2>
<p>name = {props.data.name}</p>
<p>email = {props.data.email}</p>
<p>age = {props.data.age}</p>
<p>gender = {props.data.gender}</p>
</ColumPanel>
);
}

จากโคดจะเป็นว่าผมรับค่า props มา ตอนผมส่งผมให้ data={dataHook} ผมนำมาแสดงสามารถเรียกใช้ได้ดังโคดด้านบน

ผลที่ได้จึงเป็นดังรูปต่อไปนี้

เมื่อกด submit แล้ว

จะเห็นว่าตอนกด submit ส่วน heder และ footer ไม่มีการ refresh มีการเปลี่ยนแปลงในส่วนของ content เท่านั้น

จากข้างต้นที่กล่าวมาจะเห็นว่าการสั่งผ่าน parameter จาก component หนึ่งไปยัง component หนึ่ง นั้นไม่ได้ยากเย็นเลย ยิ่งเป็นการส่งแบบ object ยิ่งสะดวก เขียนโคดสั่น คราวนี้โจทย์ถ้ามีหลายๆ component ล่ะ การจะส่ง parameter ไปทีล่ะ component ก็ดูจะเป็นเรื่องที่สิ้นคิดสิ้นดี ผมจะขอแนะนำ Redux ที่จะเก็บค่า paprmeter ให้เรา สามารถนำไปใช้ได้ทั้ง webapp กันเลยทำเดียว

เริ่มต้นด้วยการลง redux ก่อน

$npm install redux --save
$npm install react-redux --save

รูปด้านล่างแสดงวิธีการทำงานของ Redux ซึ่งประกอบได้ด้วยส่วนสำคัญดังนี้ Strore, Reducer ,Action และ View component ส่วนแสดงผลหรือเซ็ตค่า ซึ่งจะดึงค่าต่างๆจาก store ผ่าน function mapStateToProps และถ้าต้องการ เซ็ตค่าก็จะไปเรียกใช้ action ให้เซ็ตค่าไปยัง store ผ่าน function mapDispatchToProps

https://bumbu.me/simple-redux/

สิ่งแรกที่จะต้องทำถ้าเราจะใช้ redux เป็น storage คือการ สร้าง reducer วิธีการคือ สร้าง folder reducers และ ไฟล์ reducers/index.js

import { combineReducers } from 'redux';
import personReducer from './person'
export default combineReducers({
person: personReducer
});

จากโคดด้านบนมีการเรียนไฟล์ person.js ซึ่งเราจะต้องสร้างไว้ใน folder reducers นี้แหละ เพื่อใช้ในการเป็นตัวเก็บและเซ็ตข้อมูลของเรา

ในไฟล์ reducers/person.js

const initialState = {
name: '',
email: '',
age: '',
gender: ''
}
export default function personReducer(state = initialState, action){
switch (action.type) {
case 'SET':
return {
name: action.name,
email: action.email,
age: action.age,
gender: action.gender}
default:
return state
}
}

ใน personReducer จะถูกเรื่องใช้งานต่างๆตามค่า action.type ซึ่งในกรณีผมให้เป็น ถ้า action.type เป็น ‘SET’ จะมีการเซ็ตค่าตัวแปลต่างๆคือ name,email,age และ gender

หลังจากที่ผมสร้างในส่วนของ reducers เสร็จแล้วผมกลับจะไปที่ src/index.js ผมจะทำการ createStore ดังนี้

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import { createStore } from 'redux';
import { Provider } from 'react-redux'
import reducer from './reducers'
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);

ทำการ import

import { createStore } from 'redux';
import { Provider } from 'react-redux'
import reducer from './reducers'

ผมใช้ Provider store={store} ครอบ app ซึ่งเป็น component ที่บรรจุส่วนของ header, content และ footer

หลังจากสร้าง store แล้ว ต่อไปจะเริ่มที่การ set ค่าเข้าไปใน store ผมจะไปที่ content.js เมื่อกด summit แล้ให้ set ค่าทั้งหมดไปยัง store ก่อนอื่นผมต้องสร้าง function นี้ก่อน

const mapDispatchToProps = dispatch => ({
setPerson: (hookDat) => dispatch({
type: 'SET',
name: hookDat.name,
email: hookDat.email,
age: hookDat.age,
gender: hookDat.gender})
});

อย่าลืมเพิ่ม import { connect } from ‘react-redux’; เข้าไปในไฟล์ด้วยล่ะ

ฟังก์ชั่น mapDispatchToProps ใช้ dispatch ในการส่งค่า ให้ action.type เท่ากับ ‘SET’ และส่งค่าจาก hookDat ซึ่งเป็น data object state ของ component content ผ่าน action.name, action.email, action.age และ action.gender

ใน submitHandler เราเรียกใช้ props.setPerson(dataHook); ได้ดังนี้

function submitHandler(event){
event.preventDefault();
props.setPerson(dataHook);
setStOutput(true);
}

ก่อนจบไฟล์นี้เราจะต้อง export มันด้วย

export default connect(null,mapDispatchToProps)(Content);

เสร็จแล้วครับสำหรับส่วนของการ set ค่า ต่อไปเป็นส่วนแสดงผล

ผมจะให้แสดงผลใน ContentAfter component

เพิ่ม function mapStateToProps เข้าไปครับ

const mapStateToProps = state => ({
name: state.person.name,
email: state.person.email,
age: state.person.age,
gender: state.person.gender
});

ตอนเรียกใช้ค่าก็เรียกผ่าน props ได้เลยครับ จากโคดด้านล่าง props จะทำ 2 หน้าที่เลยรับค่าจาก component ก่อนหน้านั้น และ ไปเอาค่ามากจาก mapStateToProps

{props.data.name} คือ ค่าจาก content component

{props.name} คือ ค่าจาก store

function ContentAfter(props){
return (
<ColumPanel>
<h2>Listening Test</h2>
<p>name = {props.data.name} : {props.name}</p>
<p>email = {props.data.email} : {props.email}</p>
<p>age = {props.data.age} : {props.age}</p>
<p>gender = {props.data.gender} : {props.gender}</p>
</ColumPanel>
);
}
export default connect(mapStateToProps)(ContentAfter);

ไม่ว่า Component ไหนจะอยู่ใน app จะมีหรือไม่มีการส่งค่าผ่านระหว่าง component เราก็สามารถมาก get ค่าจาก store ได้

เป็นไงบ้างครับ Redux ไม่ยากอย่างที่คิดใช่ไหมครับ ผมยอมรับเลยว่าไปอ่าน tutorial ของมันแล้วงงจริงๆ 555+ ใช้เวลาอยู่สักพักเลย

--

--