0
0
Lập trình
TT

✨Mẹo React.js: Phần 2 - Thủ Thuật Tối Ưu Cho Mã Sạch

Đăng vào 4 ngày trước

• 8 phút đọc

✨ Mẹo React.js: Phần 2 - Thủ Thuật Tối Ưu Cho Mã Sạch

Chào mừng bạn trở lại! 👋 Nếu bạn đã yêu thích những mẹo React.js trong phần 1, thì giờ đây chúng ta sẽ cùng nhau khám phá những thủ thuật nâng cao hơn. Bây giờ bạn đã bắt đầu cảm thấy đau đầu với việc truyền props, luồng trạng thái phức tạp và những lần render không cần thiết.

Đây không còn là về những điều cơ bản nữa. Đây là Phần 2: Phiên bản Pro. Những mẹo này sẽ giúp bạn viết mã hiệu quả hơn, bảo trì dễ dàng hơn và thực sự thanh lịch.

Mục lục

  1. Bộ Đôi Hiệu Suất useCallback & useMemo
  2. Sức Mạnh của Custom Hook
  3. Nâng Cấp useReducer cho Trạng Thái Phức Tạp
  4. Câu Chuyện Cảnh Giác về Prop Spreading
  5. Mẹo Lazy Load & Suspense cho Tốc Độ Tối Đa
  6. Thực Hành Tốt Nhất
  7. Cạm Bẫy Thường Gặp
  8. Mẹo Tối Ưu Hiệu Suất
  9. Khắc Phục Sự Cố

1. Bộ Đôi Hiệu Suất useCallback & useMemo {#usecallback-usememo}

Vấn đề: Thành phần của bạn đang render lại mỗi khi có sự thay đổi, ngay cả khi không có gì quan trọng thay đổi. Bạn đang truyền các hàm và đối tượng như props hoặc dependencies, dẫn đến tính toán không cần thiết và render lại các thành phần con.

Giải pháp: Sử dụng useCallback cho các hàm và useMemo cho các phép tính tốn kém để ghi nhớ các giá trị và ngăn chúng được tái tạo mỗi lần render.

javascript Copy
// 🚫 Tạo lại hàm này mỗi lần render
const handleClick = () => {
  setCount(count + 1);
};

// ✅ useCallback ghi nhớ hàm
const handleClick = useCallback(() => {
  setCount(count + 1);
}, [count]); // Chỉ tái tạo nếu `count` thay đổi

// 🚫 Tính toán lại điều tốn kém này mỗi lần render
const expensiveValue = someArray.filter(...).map(...).find(...);

// ✅ useMemo ghi nhớ kết quả của phép tính
const expensiveValue = useMemo(() => {
  return someArray.filter(...).map(...).find(...);
}, [someArray]); // Chỉ tính toán lại nếu `someArray` thay đổi

Khi nào nên sử dụng: Đừng chỉ sử dụng nó một cách bừa bãi! Sử dụng những hook này khi:

  • Bạn truyền một hàm như một prop cho một thành phần con đã được tối ưu hóa (ví dụ, bọc trong React.memo).
  • Bạn thực hiện một phép toán tốn kém về mặt tính toán.
  • Giá trị đó là một dependency của một hook khác (như useEffect).

2. Sức Mạnh của Custom Hook {#custom-hook}

Vấn đề: Bạn thấy mình viết cùng một logic nhiều lần trong các thành phần khác nhau: lấy dữ liệu, xử lý form, lắng nghe kích thước cửa sổ, v.v.

Giải pháp: Custom Hooks. Tách logic thành phần của bạn thành các hàm tái sử dụng. Chúng chỉ là các hàm JavaScript có thể gọi các hook khác.

javascript Copy
// 🚫 Logic lấy dữ liệu trùng lặp trong nhiều thành phần
function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/user')
      .then((res) => res.json())
      .then(setUser)
      .finally(() => setLoading(false));
  }, []);
  // ... JSX của thành phần
}

// ✅ Tạo hook useFetch tái sử dụng
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then(setData)
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading };
}

// 🎉 Bây giờ sử dụng nó trong bất kỳ thành phần nào!
function UserProfile() {
  const { data: user, loading } = useFetch('/api/user');
  // ... JSX của thành phần sạch
}

Tại sao nó tuyệt vời: Nó làm cho các thành phần của bạn trở nên cực kỳ sạch sẽ và biến logic phức tạp thành một dòng lệnh đơn giản. Đây là hình thức tái sử dụng mã tối ưu trong React.

3. Nâng Cấp useReducer cho Trạng Thái Phức Tạp {#usereducer}

Vấn đề: useState của bạn đang trở nên khó quản lý. Bạn có nhiều phần trạng thái thay đổi cùng nhau, hoặc cập nhật trạng thái của bạn rất phức tạp với nhiều đối tượng lồng nhau.

Giải pháp: Sử dụng useReducer cho trạng thái liên quan đến logic phức tạp hoặc nhiều giá trị con. Nó giống như useState nhưng khi bạn cần một cách quản lý trạng thái chuyển tiếp mạnh mẽ và có thể dự đoán hơn.

javascript Copy
// 🚫 useState lộn xộn với các cập nhật phức tạp
const [state, setState] = useState({
  loading: false,
  data: null,
  error: null,
});

const fetchData = async () => {
  setState({ ...state, loading: true, error: null }); // 🤮
  try {
    const data = await someApiCall();
    setState({ ...state, loading: false, data: data });
  } catch (error) {
    setState({ ...state, loading: false, error: error.message });
  }
};

// ✅ useReducer sạch sẽ, dễ đoán
const initialState = { loading: false, data: null, error: null };

function reducer(state, action) {
  switch (action.type) {
    case 'FETCH_START':
      return { ...state, loading: true, error: null };
    case 'FETCH_SUCCESS':
      return { ...state, loading: false, data: action.payload };
    case 'FETCH_ERROR':
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const fetchData = async () => {
    dispatch({ type: 'FETCH_START' }); // 😍 Rõ ràng quá!
    try {
      const data = await someApiCall();
      dispatch({ type: 'FETCH_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_ERROR', payload: error.message });
    }
  };
}

Tại sao nó tuyệt vời: Nó tập trung hóa logic trạng thái của bạn, làm cho nó dễ đoán và dễ kiểm tra, và giữ cho mã thành phần của bạn sạch sẽ.

4. Câu Chuyện Cảnh Giác về Prop Spreading {#prop-spreading}

Vấn đề: Bạn đang truyền một tá props xuống thành phần con. Mã của bạn đầy rẫy propName={propName}.

javascript Copy
// 🚫 Quá lặp đi lặp lại!
<ChildComponent
  title={title}
  description={description}
  onClick={onClick}
  userId={userId}
  className="card"
  data-testid="child"
/>

Giải pháp (với CẢNH BÁO): Bạn có thể sử dụng toán tử spread {...props}. Nhưng hãy cẩn thận. Chỉ thực hiện điều này khi bạn đang cố gắng truyền tất cả các props xuống một cách có chủ đích.

javascript Copy
// ✅ Sạch hơn nhiều, nhưng BIẾT những gì bạn đang trải rộng!
const parentProps = { title, description, onClick, userId };
<ChildComponent {...parentProps} className="card" data-testid="child" />

⚠️ Cảnh báo quan trọng: Không bao giờ trải props một cách mù quáng ({...props}) từ các tham số hàm của một thành phần. Bạn có thể vô tình truyền các props không cần thiết hoặc thậm chí có hại xuống các phần tử DOM, điều này khiến React cảnh báo bạn (ví dụ, truyền một prop theme đến một <div>).

Khi nào nên sử dụng: Khi bạn đang hoạt động như một thành phần "chuyển tiếp" thuần túy hoặc xây dựng một wrapper quanh một phần tử gốc.

5. Mẹo Lazy Load & Suspense cho Tốc Độ Tối Đa {#lazy-load-suspense}

Vấn đề: Bundle JavaScript chính của bạn quá lớn vì nó chứa mọi thành phần. Người dùng của bạn đang phải chờ đợi quá lâu để tải trang ban đầu.

Giải pháp: Sử dụng React.lazySuspense để chia nhỏ mã ứng dụng của bạn và tải các thành phần chỉ khi cần thiết.

javascript Copy
// 🚫 Thành phần nặng trong bundle chính
import HeavyComponent from './HeavyComponent';

// ✅ Thành phần này được chia thành một chunk riêng và tải theo yêu cầu
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function MyApp() {
  return (
    <div>
      <Suspense fallback={<div>Đang tải một thành phần tuyệt vời...</div>}>
        {/* HeavyComponent sẽ không tải cho đến khi nó được render */}
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

Tại sao nó tuyệt vời: Nó giảm đáng kể kích thước bundle ban đầu, dẫn đến thời gian tải nhanh hơn và trải nghiệm người dùng tốt hơn. Đây là một trong những nền tảng của React hiện đại, ưu tiên hiệu suất.

Thực Hành Tốt Nhất {#best-practices}

  • Chỉ sử dụng useCallback và useMemo khi cần thiết: Việc sử dụng không đúng cách có thể dẫn đến hiệu suất kém.
  • Tạo Custom Hook cho các logic lặp lại: Giúp mã của bạn sạch sẽ và dễ duy trì hơn.
  • Sử dụng useReducer cho trạng thái phức tạp: Đảm bảo rằng logic trạng thái được quản lý tốt hơn.

Cạm Bẫy Thường Gặp {#common-pitfalls}

  • Quá lạm dụng Prop Spreading: Chỉ sử dụng khi thực sự cần thiết và hiểu rõ về các props bạn đang truyền.
  • Không sử dụng Suspense cho các thành phần không cần thiết: Điều này có thể dẫn đến trải nghiệm người dùng không tốt.

Mẹo Tối Ưu Hiệu Suất {#performance-tips}

  • Sử dụng React.memo: Để ngăn chặn các render không cần thiết cho các thành phần.
  • Kiểm tra bundle size thường xuyên: Đảm bảo rằng ứng dụng của bạn không quá nặng nề.

Khắc Phục Sự Cố {#troubleshooting}

  • Nếu bạn gặp vấn đề với re-render: Kiểm tra props và state của bạn để đảm bảo rằng chúng không thay đổi không cần thiết.
  • Nếu Lazy Load không hoạt động: Đảm bảo rằng các thành phần được nhập khẩu đúng cách và kiểm tra cấu hình Suspense.

Kết Luận

Việc thành thạo React là một hành trình. Những mẹo "pro" này không chỉ là về việc viết ít mã hơn; chúng còn về việc viết mã thông minh hơn, bền vững hơn và dễ mở rộng hơn.

Đừng cảm thấy áp lực phải sử dụng tất cả ngay lập tức. Hãy chọn một mẹo mà bạn đang gặp vấn đề và thử nghiệm với nó.

Bạn có mẹo nâng cao React yêu thích nào không? Hãy chia sẻ nó trong phần bình luận bên dưới!

Xem Phần 1 tại đây: ✨7 Mẹo React.js Mà Mọi Lập Trình Viên Mới Cần Biết

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào