Cách quản lý trạng thái hiệu quả trong React

Tạo trạng thái toàn cục có thể làm chậm hiệu suất của ứng dụng. Vì thế, hãy cùng Quantrimang.com học cách tạo và dùng trạng thái hiệu quả trong ứng dụng React nhé.

Lập trình bằng React

Nếu đã viết rất nhiều code React, có thể bạn đã từng dùng sai state - trạng thái. Một trong số những lỗi phổ biến nhất mà các lập trình viên React thường mắc phải là lưu trữ trạng thái toàn cục trong ứng dụng, thay vì để chúng trong thành phần tại nơi đã sử dụng.

Học cách bạn có thể tái cấu trúc code để dùng trạng thái cục bộ và tại sao nên làm như thế luôn là ý tưởng hay.

Ví dụ cơ bản về trạng thái trong React

Đây là một ứng dụng bộ đếm đơn giản minh họa cách trạng thái thường được xử lý trong React.

import {useState} from 'react'
import {Counter} from 'counter'

function App(){
  const [count, setCount] = useState(0)
  return <Counter count={count} setCount={setCount} />
}

export default App

Ở dòng 1 và 2, bạn nhập hook useState() để tạo trạng thái và thành phần Counter. Bạn xác định trạng thái count và phương thức setCount để cập nhật trạng thái. Sau đó, bạn chuyển cả hai xuống thành phần Counter.

Thành phần Counter sau đó xuất count và gọi setCount để tăng và giảm số đếm.

function Counter({count, setCount}) {
  return (
    <div>
      <button onClick={() => setCount(prev => prev - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(prev => prev + 1)}>+</button>
    </div>
  )
}

Bạn chưa xác định biến count và hàm setCount cục bộ trong thành phần Counter. Thay vào đó, bạn đã chuyển nó trong thành phần gốc (App). Nói cách khác, bạn đang dùng trạng thái toàn cục.

Vấn đề với trạng thái toàn cục

Vấn đề ở đây là bạn đang lưu trữ trạng thái trong thành phần gốc (hoặc cha của cha), sau đó chuyển nó xuống dưới dạng thuộc tính cho thành phần thật sự cần.

Thỉnh thoảng điều đó tốt khi bạn có một trạng thái được chia sẻ trên nhiều thành phần. Nhưng trong trường hợp này, không có thành phần nào khác quan tâm đến trạng thái count ngoại trừ thành phần Counter. Do đó, tốt hơn là chuyển trạng thái sang thành phần Counter nơi nó thực sự được sử dụng.

Di chuyển trạng thái sang thành phần con

Khi di chuyển trạng thái sang thành phần Counter, nó trông sẽ như thế này:

import {useState} from 'react'

function Counter() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <button onClick={() => setCount(prev => prev - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(prev => prev + 1)}>+</button>
    </div>
  )
}

Sau đó, bên trong thành phần App, bạn không phải chuyển xuống bất kỳ chi tiết nào sang Counter:

// imports
function App(){
  return <Counter />
}

Bộ đếm sẽ hoạt động chính xác giống như nó đã làm trước đây, nhưng khác biệt lớn ở đây là tất cả trạng thái nằm cục bộ bên trong Counter này. Vì thế, nếu cần có bộ đếm khác trên trang chủ, bạn phải có 2 bộ đếm độc lập. Mỗi bộ đếm đều khép kín và đảm nhận tất cả trạng thái của nó.

Xử lý trạng thái trong các ứng dụng phức tạp hơn

Tình huống khác tại nơi bạn dùng trạng thái toàn cục là với các biểu mẫu. Thành phần App bên dưới chuyển dữ liệu biểu mẫu (email và mật khẩu) cùng phương thức setter xuống thành phần LoginForm.

import { useState } from "react";
import { LoginForm } from "./LoginForm";

function App() {
  const [formData, setFormData] = useState({
     email: "",
     password: "",
  });

 function updateFormData(newData) {
    setFormData((prev) => {
      return { ...prev, ...newData };
    });
  }

 function onSubmit() {
    console.log(formData);
  }

 return (
    <LoginForm
      data={formData}
      updateData={updateFormData}
      onSubmit={onSubmit}
    />
  );
}

Thành phần LoginForm nhận thông tin đăng nhập và hiển thị nó. Khi bạn gửi biểu mẫu, nó gọi hàm updateData, được chuyển xuống từ thành phần gốc (chính).

function LoginForm({ onSubmit, data, updateData }) {
  function handleSubmit(e) {
    e.preventDefault();
    onSubmit();
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email</label>
      <input
        type="email"
        id="email"
        value={data.email}
        onChange={(e) => updateData({ email: e.target.value })}
      />
      <label htmlFor="password">Password</label>
      <input
        type="password"
        id="password"
        value={data.password}
        onChange={(e) => updateData({ password: e.target.value })}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Thay vì quản lý trạng thái trên thành phần gốc, bạn nên chuyển trạng thái sang Login Form.js sẽ tốt hơn. Đây là nơi bạn dùng code này. Hành động này sẽ tạo thành phần độc lập và không phụ thuộc vào thành phần khác để lấy dữ liệu. Đây là phiên bản đã chỉnh sửa của LoginForm:

import { useRef } from "react";

function LoginForm({ onSubmit }) {
  const emailRef = useRef();
  const passwordRef = useRef();
  
 function handleSubmit(e) {
    e.preventDefault();
    onSubmit({
      email: emailRef.current.value,
      password: passwordRef.current.value,
    });
  }

 return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email</label>
      <input type="email" id="email" ref={emailRef} />
      <label htmlFor="password">Password</label>
      <input type="password" id="password" ref={passwordRef} />
      <button type="submit">Submit</button>
    </form>
  );
}

Tại đây, bạn liên kết đầu vào một biến bằng thuộc tính ref và hook React useRef, thay vì chuyển trực tiếp các phương thức cập nhật. Điều này giúp bạn loại bỏ code dài dòng và tối ưu hóa hoạt động của biểu mẫu bằng hook useRef.

Trong thành phần chính (App.js), bạn có thể loại bỏ cả trạng thái toàn cục và updateFormData() vì không còn cần tới nó nữa. Hàm duy nhất còn lại là onSubmit(), mà bạn gọi từ bên trong thành phần LoginForm để ghi lại chi tiết đăng nhập trên console.

function App() {
  function onSubmit(formData) {
    console.log(formData);
  }

 return (
    <LoginForm
      data={formData}
      updateData={updateFormData}
      onSubmit={onSubmit}
    />
  );
}

Không chỉ biến trạng thái của bạn thành cục bộ khi có thể, mà còn thực sự loại bỏ nhu cầu cần tới trạng thái bất kỳ. Nhờ đó, thành phần App đã được đơn giản hóa đáng kể (chỉ có một hàm).

Thành phần LoginForm của bạn cũng đơn giản hơn vì bạn không cần phải lo update trạng thái. Thay vào đó, bạn chỉ cần theo dõi hai refs. Thế là xong.

Trên đây là hướng dẫn quản lý trạng thái - state hiệu quả trong React. Hi vọng bài viết hữu ích với các bạn.

Thứ Sáu, 09/06/2023 14:21
51 👨 133
0 Bình luận
Sắp xếp theo