React-Redux useSelector + useDispatch hay Connect
TIL
770
reactjs
58
redux
17
White

HungHayHo viết ngày 12/07/2020

React-Redux useSelector + useDispatch hay Connect

Mấy tuần trước có một cậu trong team tớ hỏi: "Tại sao không sử dụng useSelector cho dự án mới?" Nếu các bạn sử dụng React và Redux chắc các bạn sẽ dùng thêm một thứ khá phổ biến đi kèm với 2 thư viện này, đó chính là thư viện react-redux.

react-redux cho phép bạn dùng hàm connect để lấy các state trong reducer hoặc sử dụng các action trên các components để hiển thị. Cụ thể mục tiêu này đạt được nhờ dùng đến 2 hàm là mapStateToPropsmapDispatchToProps.

Từ phiên bản react-redux v7 bạn đã có thể sử dụng useSelectoruseDispatch thay thế cho hàm connect. Một lý cậu đồng nghiệp sử dụng 2 hàm này là cú pháp code của bạn trở nên sáng sủa hơn. Và tớ đã viết thử 1 todo app đơn giản để kiểm chứng điều này.

Todos.js

import React from "react";
import { connect } from "react-redux";
import { getTodoById, removeTodo } from "../reducer";
import "./style.css";

function Todo({ todo, removeTodo }) {
  console.log("render Todo");

  function handleRemoveTodo() {
    removeTodo(todo.id);
  }
  return (
    <div className="container">
      <div className="content">{todo.content}</div>
      <button onClick={handleRemoveTodo} style= { {  marginLeft: 10, height: 20 }}>
        X
      </button>
    </div>
  );
}

function mapState(state, ownProps) {
  const { id } = ownProps;
  return {
    todo: getTodoById(state, id),
  };
}

export default connect(mapState, { removeTodo })(Todo);

Todo.js

import React from "react";
import { connect } from "react-redux";
import { getTodoById, removeTodo } from "../reducer";
import "./style.css";

function Todo({ todo, removeTodo }) {
  console.log("render Todo");

  function handleRemoveTodo() {
    removeTodo(todo.id);
  }
  return (
    <div className="container">
      <div className="content">{todo.content}</div>
      <button onClick={handleRemoveTodo} style= { {  marginLeft: 10, height: 20 }}>
        X
      </button>
    </div>
  );
}

function mapState(state, ownProps) {
  const { id } = ownProps;
  return {
    todo: getTodoById(state, id),
  };
}

export default connect(mapState, { removeTodo })(Todo);

So với việc sử dụng useSelectoruseDispatch thì

Todos.js

import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { v4 as uuid } from "uuid";
import { getTodos, addTodo } from "../reducer";
import Todo from "./Todo";

function Todos() {
  const [task, setTask] = useState("");
  const todos = useSelector((state) => getTodos(state));
  const dispatch = useDispatch();

  function handleChange(event) {
    setTask(event.target.value);
  }

  function handleClick(event) {
    event.preventDefault();
    const payload = {
      id: uuid(),
      content: task,
    };
    dispatch(addTodo(payload));
    setTask("");
  }

  return (
    <>
      <form onSubmit={handleClick}>
        New task: &nbsp;
        <input type="text" onChange={handleChange} value={task} />
        &nbsp; &nbsp;
        <button type="submit">+</button>
      </form>
      <br />

      <div>
        {!!todos.length &&
          todos.map((todo) => <Todo key={todo.id} id={todo.id} />)}
      </div>
    </>
  );
}

export default Todos;

Todo.js

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { getTodoById, removeTodo } from "../reducer";
import "./style.css";

function Todo({ id }) {
  const todo = useSelector((state) => getTodoById(state, id));
  const dispatch = useDispatch();

  console.log("redner Todo");

  function handleRemoveTodo() {
    dispatch(removeTodo(todo.id));
  }
  return (
    <div className="container">
      <div className="content">{todo.content}</div>
      <button onClick={handleRemoveTodo} style= { {  marginLeft: 10, height: 20 }}>
        X
      </button>
    </div>
  );
}

export default Todo;

Có thể thấy rằng số dòng code giảm đi, nhất là khi nếu bạn sử dụng nhiều state của reducer hay action khiến cho hàm mapStateToPropsmapDispatchToProps phình to ra, cộng thêm việc destructure các biến trong props để sử dụng ở đầu hàm (như dòng 7 file Todo.js có connect) khiến cho số lượng code bạn viết tăng lên tương đối nhiều.

Tuy nhiên useSelector có một nhược điểm là khi một component thay đổi, nó khiến cho toàn bộ cây component phải render lại và việc này gây tốn hiệu năng.

Số lần render dùng connect

Số lần rende dùng useSelector

Do đó mà ngay trong document của react-redux, React.memo được sử dụng để hạn chế số lần render. Todo sẽ trở thành như sau

Todo.js

import React from "react";
import { getTodoById, removeTodo } from "../reducer";
import { useSelector, useDispatch } from "react-redux";
import "./style.css";

const Todo = React.memo(function ({ id }) {
  const todo = useSelector((state) => getTodoById(state, id));
  const dispatch = useDispatch();

  console.log("render Todo");

  function handleRemoveTodo() {
    dispatch(removeTodo(todo.id));
  }
  return (
    <div className="container">
      <div className="content">{todo.content}</div>
      <button onClick={handleRemoveTodo} style= { {  marginLeft: 10, height: 20 }}>
        X
      </button>
    </div>
  );
});

export default Todo;

Việc dùng React.memo hạn chế số lần render useSelector nhưng rõ ràng là cách làm này khiến useSelector phải dựa vào một phương thức khác để không bị mất đi hiệu năng so với connect, đây là một điểm trừ. Một cách khắc phục khác là thêm hàm shallowEqual như param thứ 2 của useSelector nhưng không phải lúc nào hàm này cũng chạy (như trong ví dụ hiện tại). Đây là lý do mà useSelector hay useDispatch chưa được sử dụng phổ biến

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

HungHayHo

9 bài viết.
2 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
3 0
Log 1 1. At the beginning of the chapter, the role of function is emphasized and two ways of creating a function are introduced: function declarat...
HungHayHo viết hơn 2 năm trước
3 0
White
3 0
Log 2 1. Phần đầu chap này hứa rằng mình sẽ biết một vài 'common coding idioms' và hai khái niệm anonymous function và closure được nhấn mạnh. Ano...
HungHayHo viết hơn 2 năm trước
3 0
White
2 0
Log 3 This is not the first time I read about Object Construction but last time was so brief that now I can barely recall anything except a metaph...
HungHayHo viết hơn 2 năm trước
2 0
Bài viết liên quan
White
5 2
Trải qua 1 thời gian khá dài trải nghiệm Angular, mình thấy RxJS khá hay. Bài này sẽ quay trở lại với bài toán Counter thần kì của React/Redux, như...
cdxf viết hơn 2 năm trước
5 2
White
17 3
(Link) (Link) &mdash; tracks device battery state. (Link) (Link) &mdash; tracks geo location state of user's device. (Link) (Link) &mdash; tr...
Đinh Viễn viết hơn 1 năm trước
17 3
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
9 bài viết.
2 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!