ชีวิตจะไม่ยากอีกแล้วเมื่อมี Redux Toolkit

NSLog0
NSLog0
Aug 23 · 3 min read
Image for post
Image for post

บทความนี้ผมจะมาสอนวิธีการใช้ตัว Redux Toolkit แทนการใช้ Redux core ผมเชื่อว่าหลายๆ คนเจอปัญหาว่าการใช้งาน React กับ Redux นั้นยากมาก ทั้งมือใหม่และมือเก่า ในช่วงแรกๆ เสียเวลาชีวิตสุดๆ เลย กว่าจะเข้าใจเรื่อง Stores, Actions, Reducers, Action Creators ได้หมดเวลาไปหลาย ชม. และถ้าเกิดว่าตัว Action Creators เราดันเขียนมันให้แบบ Return function หรือ promise แทนการโยน Action กลับออกมาจะต้องมาใช้ Redux-thunk อีก (ใครนึกภาพไม่ออกแนะนำไปอ่านคอมเม้นของ Dan Abramov ผู้สร้าง Redux ได้ที่ Stackoverflow) [บทความอื่นๆ link] และวันนี้เราจะมาเขียนโค้ดให้เสร็จภายในไม่ถึง ชม. กัน ด้วย Redux Toolkit

Let’s get started

สำหรับใครที่ใช้ create-react-app แล้วอยากได้ตัว starter ที่มีโค้ดของ Redux toolkit ติดตั้งมาให้ แนะนำให้ติดตั้ง create-react-app ด้วยคำสั่งนี้

npx create-react-app [you-app-name] --template redux

และใครใช้ Next.js ก็ใช้คำสั่งนี้เลยครับ

npx create-next-app --example with-redux-toolkit [your-app-name]

สำหรับใครที่มีโปรเจคอยู่แล้วก็ใช้ npm หรือ yarn ติดตั้งเข้าไปได้เลย

# NPMnpm install @reduxjs/toolkit# Yarnyarn add @reduxjs/toolkit

Project Structure

ผมจะยึด Structure นี้เป็นหลักในการสอนครั้งนี้ เวลาพูดถึง Snippet ไหนๆ ผมจะมี location ไฟล์ไว้ให้ จะได้ไม่งง

src
|
| - libs
| |
| | - todoSlice.js
|
| - store.js
|
| - pages
| | - Todo.js
|
|- App.js
|- index.js

createSlice

เราจะมาพูดเรื่องพื้นฐานสุดๆ ในการสร้าง Store, Action, Reducer ด้วย createSlice กัน ตัว createSlice มีพารามิเตอร์หลักๆ อยู่ 3 ตัว (เราจะพูดกันแค่ 3 ก่อนเอาที่จำเป็นจริงๆ สำหรับมือใหม่ก่อน)

  • name เป็นชื่อของ Slice นั้นๆ หรือจะมองว่าเป็นชื่อของ Store ก็ได้
  • initialState ค่าเริ่มของ state เพื่อให้เมื่อ reducer มาใช้งานจะได้เรียกเอา state ออกมาใช้ก่อนตอนที่เริ่มระบบ
  • reducer ถ้าเป็นการเขียน Redux แบบเก่าเราจะต้องการระบุ Action ว่าจะหากมี Action อะไรเกิดขึ้นให้ไปเรียก reducer ตัวไหนเพื่อจัดการกับ State หรือพูดง่ายๆ ว่า Action คือตัวที่บอกระบบว่าเกิดอะไรขึ้น (I need Add Item, I need edit item) และ Reducer เป็นตัวที่เข้ามาจัดการกับ State (doAddIitem, doEditItem) เมื่อเกิด Action บางอย่างขึ้น

เริ่มเขียนโค้ดกัน เริ่มที่การสร้าง Slice ก่อน

// src/libs/todoSlice.jsimport { createSlice } from '@reduxjs/toolkit'export const todoSlice = createSlice({
name: 'todos',
initialState: {
items: [],
},
reducers: {
addItem: (state, action) => {
state.todo.push(action.payload)
},
setComplete: (state, action) => {
const todo = state.find(todo => todo.id === action.payload)
if (todo) {
todo.completed = !todo.completed
}
},
},
})
export default todoSlice.reducer;

จากนั้นไฟล์เดียวกันเราจะมาสร้าง Action กัน โดยเอามาจาก reducer

// src/libs/todoSlice.jsexport const {
addItem,
setComplete,
} = todoSlice.actions

สุดท้ายพอเราสามารถอัพเดท State เราก็ต้องสามารถดึง State มาโชว์ได้ด้วย เราจะมาสร้างฟังก์ชันไว้ดึง State กัน

// src/libs/todoSlice.jsexport const getTodos = (state) => state.todos.items

Config store

ต่อไปเราก็เอา Reducer มาใส่ใน Store โดยการ import ไฟล์ Slice ที่เราทำ จากนั้นก็เขียนโค้ดไปตามนี้เลย ถ้ามีหลาย Slice เราก็ Import เข้ามาใส่ทีละตัวครับ

// src/store.jsimport { configureStore } from '@reduxjs/toolkit';
import todoReducer from './libs/todoSlice';
export default configureStore({
reducer: {
todos: todoReducer,
},
});

ข้อควรจำ: ชื่อของ slice ต้องเหมือนกัน key ของ reducer นะครับ

Config Provider

เพื่อให้มันทำงานได้สมบูรณ์เราก็ต้องมาสร้าง Provider ให้มันด้วยเอาไว้ที่ index.js สำหรับใครที่สร้างโปรเจคแบบตั้งมาเอง หรือ create-react-app นะ สำหรับใครใช้ nextjs ก็เอาไว้ใน _app.js ใต้โฟร์เดอร์ pages ครับ

// src/index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
document.getElementById('root')
);

เท่านี้เราก็มี Redux ไว้ใช้งานแล้วครับ ต่อไปเราจะเอามาใส่ใน component กัน

Using in component

// src/pages/Todo.jsimport React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
addItem,
setComplete,
getTodos,
} from '../libs/todoSlice'
export function Todo() {
const todos = useSelector(getTodos)
const dispatch = useDispatch()
const [todoTitle, setTodoTitle] = useState('')
return (
<>
{
todos.map((todo, idx) => {
<div key={idx}>
<p>{todo.title}</p>
<button
onClick={() => dispatch(setComplete(idx))}
>
Done
</button>
</div>
})
}
<div>
<input
type="text"
value={todoTitle}
onChange={(e) => setTodoTitle(e.target.value)}
/>
<button
onClick={() => dispatch(addItem({
title: todoTitle,
complete: false
}))}
>
Add
</button>
</div>
</>
)

ก่อนอื่นเราต้อง import ฟังก์ชันใน Slice เข้ามาก่อน และเมื่อเราต้องการจะเซตค่าให้กับ Redux ให้เรียกผ่าน dispatch โดยเราสร้างมาจาก useDispatch() เท่านี้เราก็สามารถเชื่อมกับ Redux ได้แล้วครับ

หากลองสังเกตว่าผมมีการใช้ useState ด้วย นั้นหมายถึงเราสามารถใช้การเก็บ State อยู่ใน component นั้นๆ (Local state) ก็ได้ก่อนที่เราจะทำการส่งไปเก็บที่ Redux (Global state) ครับ

Final

ถ้าดูจากโครงสร้างของโปรเจคด้านบนจะเห็นว่าตัว index.js กับ App.js แยกกันแล้วผมก็ import App.js ไปไว้ใน index.js เพราะงั้นเราต้องเอา Component ของเรามาไว้ใน App.js ก่อน หรือแล้วแต่ใครจะออกแบบนะครับ แต่ต้องมี Provider ครอบไว้เหมือนที่ผมทำใน index.js ด้วยนะไม่งั้นมันจะทำงานไม่ได้

import React from 'react';
import { Todo } from './pages/Todo'
function App() {
return (
<div className="App">
<Todo />
</div>
);
}
export default App

เห็นไหมครับว่าการใช้ Redux tookit ทำให้โค้ดเราสั้นลงและง่ายขึ้นมากๆ แถมไม่ต้องมาคอยตั้งชื่อ Action ด้วย เพราะ createSlice() จะทำให้เราเองครับ เราก็ยังใช้ DevTool สามารถ Debug ได้เหมือนเดิมเลยครับ สำหรับบทความนี้เป็นแค่พื้นฐานให้ทำความรู้จักก่อน ในขั้นสูงกว่านี้เราสามารถเข้าไปอ่านได้ในเว็บ Redux ได้เลยครับ

โค้ดทั้งหมดอยู่ใน https://github.com/NSLog0/template-redux-toolkit-101 แล้วครับ ลองเข้าไปดูตัวอย่างกันได้

AlgorithmTut

May the force be with you. **Tut stand for Tutorial**

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store