โดย "จูโบเย จอห์นสัน"

useReducer React hook นั้นซับซ้อนและซับซ้อนเล็กน้อยในการปรับขนาดในตอนแรก ถึงกระนั้น หลังจากที่คุณเข้าใจเบ็ดนี้และวิธีการใช้งานแล้ว มันทำให้การจัดการสถานะง่ายที่สุดเท่าที่จะเป็นไปได้ โดยเฉพาะอย่างยิ่งในด้านการติดตามของสถานะส่วนต่าง ๆ ที่ใช้งานโดยใช้เบ็ด useState

ในบทความนี้ เราจะดูที่ฮุก useReducer และเหตุใดจึงเป็นตัวเลือกที่ดีกว่าสำหรับการจัดการสถานะที่ซับซ้อนใน React มากกว่าฮุก useState บทช่วยสอนนี้เหมาะสำหรับผู้เริ่มต้น และคุณต้องติดตั้ง Node.js และ React

การจัดการของรัฐ

เรามักจะได้ยินเกี่ยวกับสถานะ การเปลี่ยนแปลงของรัฐ และการจัดการของรัฐ รัฐคืออะไรกันแน่? เราเข้าใจสิ่งนี้อย่างแท้จริงโดยบอกว่านี่คือสถานะปัจจุบันของโปรแกรมของคุณ แต่นั่นอาจไม่ง่ายเกินไปที่จะเข้าใจ ในการเขียนโปรแกรม สถานะเป็นเพียงการรวมกันของข้อมูลทั้งหมดที่เรามีในแอปพลิเคชันของเรา ซึ่งเป็นข้อมูลที่ใช้และส่งคืนโดยโปรแกรมที่กำลังดำเนินอยู่ของคุณ

การจัดการของรัฐคืออะไรกันแน่? ตามวิกิพีเดีย “ การจัดการสถานะ หมายถึงการจัดการสถานะของการควบคุมอินเทอร์เฟซผู้ใช้ตั้งแต่หนึ่งรายการขึ้นไป เช่น ช่องข้อความ ปุ่มตกลง ปุ่มตัวเลือก ฯลฯ”

useReduce เทียบกับ useState

การจัดการสถานะสามารถทำได้โดยไม่ต้องใช้ useState หรือไม่ คำถามที่แพร่หลายซึ่งมีผลลัพธ์มากกว่า 300,000 รายการบน Google:

หากคุณพบว่าตัวเองติดตามสถานะหลายส่วนที่ต้องอาศัยตรรกะที่ซับซ้อน เบ็ด useReducer อาจจะดีกว่า มาสร้างแอปที่สามารถเพิ่มและลดจำนวนโดยใช้เบ็ดนั้นและดูว่ามันจะมีประสิทธิภาพเพียงใด

การตั้งค่าพื้นที่พัฒนา

เราจำเป็นต้องเรียกใช้สิ่งนี้:

npx create-react-app counter
cd counter
npm start

หลังจากติดตั้งแล้วเราควรจะมีสิ่งนี้

ใช้ลด

const [state, dispatch] = useReducer(reducer, initialArg, init);

UseReducer ส่งคืนอาร์เรย์โดยองค์ประกอบแรกเป็นสถานะและองค์ประกอบที่สองเป็นฟังก์ชันการจัดส่งที่จะเรียกใช้ useReducer

ในการสร้างแอปพลิเคชันตัวนับ เราจำเป็นต้องมีไฟล์สี่ไฟล์: ไฟล์หลัก App.js เพื่อเรนเดอร์ส่วนประกอบของเรา Counter.js สำหรับการสมัครโต้แย้งของเรา Reducer.js โดยที่เราจะจัดการสถานะแอปพลิเคชันโดยใช้ตรรกะ useReducer ของเรา และ Styles.css ของเรา คำถามเกิดขึ้น: เรากำลังทำอะไรอยู่? เราจะบริหารจัดการรัฐอย่างไร? สิ่งนี้จะให้ประโยชน์อะไรบ้างมากกว่า useState และคำถามก็ดำเนินต่อไป ฉันจะตอบคำถามเหล่านี้ทั้งหมดในบทความนี้

เล่นซ้ำเซสชันโอเพ่นซอร์ส

OpenReplay เป็นชุดการเล่นซ้ำเซสชันแบบโอเพ่นซอร์สที่ช่วยให้คุณเห็นว่าผู้ใช้ทำอะไรบนเว็บแอปของคุณ ซึ่งช่วยให้คุณแก้ไขปัญหาได้เร็วขึ้น OpenReplay โฮสต์ในตัวเองเพื่อการควบคุมข้อมูลของคุณอย่างสมบูรณ์

เริ่มเพลิดเพลินกับประสบการณ์การแก้ไขข้อบกพร่องของคุณ — เริ่มใช้ OpenReplay ฟรี.

การสมัครเคาน์เตอร์

นี่คือวิธีที่เราสามารถเริ่มต้นสิ่งนี้ได้

const [count, dispatch] = useReducer(reducer, 0);

แทนที่จะเป็นตัวตั้งค่าใน useState เราใช้ dispatch 'Dispatch' ในที่นี้มีความหมายตามตัวอักษร เหมือนกับว่าคุณต้องการส่งอะไรบางอย่างมากกว่า: คุณสามารถพูดว่า 'send an action' เราจะประมวลผลด้วยฟังก์ชันตัวลด ดังที่เราเห็นด้านบนนี้ เรามีสถานะ 0 มาเริ่มสร้างแอปพลิเคชันตัวนับกันดีกว่า

//counter.js
import React, { useReducer } from "react";
import reducer from "./Reducer";

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);

  return (
    <div className="container">
      <div className="card">
        <h1>Counter Application</h1>
        <h3>{count}</h3>
        <div>
          <button className="btn1" onClick={() => dispatch("increment")}>
            increment
          </button>

          <button className="btn2" onClick={() => dispatch("decrement")}>
            decrement
          </button>

          <button className="btn3" onClick={() => dispatch("reset")}>
            Reset
          </button>
        </div>
      </div>
    </div>
  );
}

export default Counter;

เรายังมี:

//reducer.js

const reducer = (state, action) => {
  if (action === "increment") {
    return state + 1;
  } else if (action === "decrement") {
    return state - 1;
  } else if (action === "reset") {
    return 0;
  } else {
    throw new Error();
  }
};

export default reducer;

และสไตล์:

//styles.css

.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

h3 {
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn1 {
  background-color: blue;
  margin: 20px;
  color: beige;
}

.btn2 {
  background-color: red;
  margin: 20px;
  color: beige
}

.btn3 {
  background-color: green;
  margin: 20px;
  color: beige
}

สุดท้าย ไฟล์ App หลักของเรา

//App.js

import React from "react";
import "./styles.css";
import Counter from "./Counter";

function App() {
  return (
    <div>
      <Counter />
    </div>
  );
}

export default App;

โค้ดด้านบนแสดงแอปพลิเคชันตัวนับซึ่งสถานะได้รับการจัดการโดยตะขอ useReducer ตัวนับไม่ได้สอนมากนักเกี่ยวกับการจัดการสถานะที่ซับซ้อน แต่ฉันจะอธิบายตรรกะที่ใช้ข้างต้น ตัวลดใช้เวลาในสถานะของเราและการดำเนินการที่ส่งไป ใน Reducer.js ฟังก์ชันตัวลดจะเข้าสู่สถานะของเรา และการดำเนินการจะถูกจัดส่ง จากนั้นเราจะใช้คำสั่งแบบมีเงื่อนไข เรามี if-else ของเราและผ่านไปใน action.type ของเรา เราส่ง increment, decrement และ reset ไปยังฟังก์ชัน onclick ใน JSX

ขณะนี้เราสามารถทดสอบแอปพลิเคชันของเราได้แล้วหากทำงานได้ดี เริ่มแรกเรามี:

หลังจากเพิ่มขึ้นทีละน้อย เราจะได้:

นี่คือจุดที่เราเริ่มเห็น useReducer ส่องแสงมาที่เรา เราอาจไม่ได้สังเกตว่าเราได้แยกตรรกะการอัปเดตสถานะของเราออกจากส่วนประกอบของเราโดยสิ้นเชิง ขณะนี้เรากำลังจับคู่การกระทำกับการเปลี่ยนสถานะ และตอนนี้เราสามารถแยกวิธีที่สถานะอัปเดตจากการกระทำที่เกิดขึ้นได้ (เราจะเจาะลึกถึงประโยชน์เชิงปฏิบัติมากขึ้นในภายหลัง) สำหรับตอนนี้ เรามาเพิ่มคุณสมบัติที่ซับซ้อนมากขึ้นให้กับแอปของเราเพื่ออธิบายให้ดีขึ้นว่า useReducer สะดวกสบายเพียงใด

แทนที่จะเพิ่มหรือลดทีละ 1 มาสร้างแถบเลื่อนที่ผู้ใช้สามารถเลือกค่าที่ต้องการเพิ่มหรือลดได้ตั้งแต่ 1 ถึง 100

import React, { useState } from "react";

function Slider({ onchange, min, max }) {
  const [value, setvalue] = useState(1);

  return (
    <div className="slide">
      {value}
      <input
        type="range"
        min={min}
        max={max}
        value={value}
        onChange={(e) => {
          const value = Number(e.target.value);
          onchange(value);
          setvalue(value);
        }}
      />
    </div>
  );
}

export default Slider;

เราจำเป็นต้องนำเข้าสิ่งนี้ใน Counter.js ของเราเพื่อให้สามารถแสดงผลบนเบราว์เซอร์ได้ นอกจากนี้ เราจะส่งผ่านอุปกรณ์ประกอบฉาก min, max และ onchange ที่ให้ค่าเหล่านั้นด้วย

import Slide from "./Slide";

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });

  return (
    <div className='container'>
        <div className='card'>
            <h1>Counter Application</h1>
            <h3>{state.count}</h3>
            <div >
                <button className='btn1' onClick={() => dispatch('increment')}>increment</button>
                <button className='btn2' onClick={() => dispatch('decrement')}>decrement</button>
                <button className='btn3' onClick={() => dispatch('reset')}>Reset</button>
            </div>
            <div>
                <Slide
                    min={1}
                    max={100}
                    onchange={()=>({})}
                />
            </div>
        </div>
    </div>
  );
}

นี่คือสิ่งที่เราได้รับ

ตอนนี้เราสามารถรับค่าของตัวเลื่อนได้จากเสา onChange สิ่งนี้ช่วยให้เราตัดสินใจว่าเราจะเพิ่มและลดค่าได้มากเพียงใด เราจำเป็นต้องทำการเปลี่ยนแปลงเล็กน้อยเพื่อจัดการส่วนของสถานะของค่าตัวเลื่อนของเรา และเปิดใช้งานตัวเลื่อนของเราเพื่อกำหนดสิ่งที่เราจะเพิ่มหรือลด

มาทำให้สถานะของเราเป็นอ็อบเจ็กต์กันดีกว่า ดังนั้น สถานะใหม่ใดๆ ที่ตัวลดของเราต้องจัดการสามารถไปเป็นทรัพย์สินของวัตถุนั้นได้ ขั้นแรก เราเปลี่ยนสถานะเริ่มต้นของเราให้เป็นวัตถุ

const [state, dispatch] = useReducer(reducer, { count: 0, move: 1 });

รัฐของเราเป็นวัตถุ เราจำเป็นต้องอัปเดตตัวลดของเราเพื่อส่งคืนวัตถุที่มีคุณสมบัติสองประการ

const reducer = (state, action) => {
  if (action === "increment") {
    return {
      count: state.count + 1,
      move: state.move,
    };
  } else if (action === "decrement") {
    return {
      count: state.count - 1,
      move: state.move,
    };
  } else if (action === "reset") {
    return {
      count: 0,
      move: state.move,
    };
  } else {
    throw new Error();
  }
};
export default reducer;

กลับมาที่เคาน์เตอร์ เราต้องส่งสถานะไปที่ JSX ของเรา

<h3>{state.count}</h3>

มันใช้งานได้ดี แต่แทนที่จะให้สถานะของเราเป็นจำนวนเต็ม ตอนนี้เรามีมันเป็นวัตถุ ซึ่งช่วยให้เราสามารถส่งผ่านคุณสมบัติอื่น ๆ ได้ ตอนนี้คำถามก็ปรากฏขึ้น: คุณต้องการส่งอะไรใน onChange เพื่ออัปเดตสถานะของตัวลดของเรา ถึงตอนนี้ เราสามารถส่งประเภทของการกระทำที่เกิดขึ้นได้ (เพิ่มขึ้น ลด หรือรีเซ็ต) นั่นใช้ได้ผลดี แต่ตอนนี้เรากำลังพบกับข้อจำกัดของมัน นอกจาก action type แล้ว ยังจำเป็นต้องมีข้อมูลเพิ่มเติมอีกด้วย โดยเฉพาะเราต้องส่งผ่านค่าของสไลด์เพื่อเพิ่มลงในค่าสถานะของเราและอัปเดตสถานะ แทนที่จะให้การกระทำของเราส่งผ่านเป็นสตริง มาเปลี่ยนเป็นวัตถุที่มีคุณสมบัติประเภทกันดีกว่า ด้วยวิธีนี้ เรายังคงสามารถจัดส่งตามประเภทการดำเนินการได้ เราจะสามารถส่งค่าของแถบเลื่อนและข้อมูลอื่น ๆ เป็นคุณสมบัติในวัตถุการกระทำได้ เราสามารถไปที่ onChange prop ของเราและทำสิ่งนี้ให้เสร็จได้ทันที

<Slide
  min={1}
  max={100}
  onchange={(value) =>
    dispatch({
      type: "stepUpdate",
      step: value,
    })
  }
/>;

มีการเปลี่ยนแปลงสามประการที่เราต้องทำกับตัวลดของเรา:

  • เราจำเป็นต้องอัปเดตส่วนเพิ่มและส่วนลดเพื่อปรับการนับตามคุณสมบัติขั้นตอน ไม่ใช่แค่ 1 เราทำสิ่งนี้โดยอัปเดตด้วยอะไรก็ตามที่ move เป็น
  • เราจำเป็นต้องคำนึงถึงประเภทการกระทำใหม่ moveUpdate ด้วยการเพิ่มตัวพิมพ์ลงในตัวลดของเรา
  • เราจำเป็นต้องเปลี่ยน action ให้เป็นวัตถุแทนที่จะเป็นสตริงโดยส่งคุณสมบัติ type ไปยังกรณีใหม่ของเราเท่านั้น

มาทำการแก้ไขด่วนเหล่านั้นกัน

//Reducer.js

const reducer = (state, action) => {
  if (action === "increment") {
    return {
      count: state.count + state.move,
      move: state.move,
    };
  } else if (action === "decrement") {
    return {
      count: state.count - state.move,
      move: state.move,
    };
  } else if (action === "reset") {
    return {
      count: 0,
      move: state.move,
    };
  } else if (action.type === "moveUpdate") {
    return {
      count: state.count,
      move: action.move,
    };
  } else {
    throw new Error();
  }
};

export default reducer;

ขณะนี้เราสามารถอัปเดตค่าการนับได้โดยใช้แถบเลื่อน เช่น เพิ่มจากศูนย์ก่อนเป็น 31 และจากนั้นเป็น 48

บทสรุป

เรามาถึงจุดสิ้นสุดของบทความแล้ว แต่ฉันต้องอธิบายบางสิ่งที่สำคัญอย่างชัดเจน เราได้เห็นคุณประโยชน์อันเหลือเชื่อและทรงพลังของ useReducer ที่คุณอาจพลาดไปแล้ว: ฟังก์ชันตัวลดส่งผ่านสถานะปัจจุบันเป็นอาร์กิวเมนต์แรก ด้วยเหตุนี้ จึงเป็นเรื่องง่ายที่จะอัปเดตส่วนหนึ่งของสถานะ โดยขึ้นอยู่กับมูลค่าของสถานะอีกส่วนหนึ่ง สำหรับสิ่งนี้ คุณจะต้องใช้ฮุก useReducer แทน useState ในตัวอย่างของเรา เราอาจเห็นสิ่งนี้เมื่ออัปเดต count ตามค่าของ `move '

บทความเพิ่มเติมจากบล็อก OpenReplay

แบบฟอร์มที่เปิดใช้งานด้วยเสียงใน React with Speechly

เรียนรู้วิธีกรอกแบบฟอร์มด้วยการพูดด้วย React และ Speechly

4 พฤษภาคม 2022 · อ่าน 4 นาที

เผยแพร่ครั้งแรกที่ blog.openreplay.com เมื่อวันที่ 5 พฤษภาคม 2022