Thêm tính tương tác

Một số thứ trên màn hình cập nhật để đáp ứng đầu vào của người dùng. Ví dụ: nhấp vào thư viện ảnh sẽ chuyển ảnh đang hoạt động. Trong React, dữ liệu thay đổi theo thời gian được gọi là state (trạng thái). Bạn có thể thêm trạng thái vào bất kỳ component nào và cập nhật nó khi cần. Trong chương này, bạn sẽ học cách viết các component xử lý tương tác, cập nhật trạng thái của chúng và hiển thị các đầu ra khác nhau theo thời gian.

Phản hồi các sự kiện

React cho phép bạn thêm trình xử lý sự kiện vào JSX của mình. Trình xử lý sự kiện là các hàm của riêng bạn sẽ được kích hoạt để đáp ứng các tương tác của người dùng như nhấp, di chuột, tập trung vào các đầu vào biểu mẫu, v.v.

Các component tích hợp sẵn như <button> chỉ hỗ trợ các sự kiện trình duyệt tích hợp sẵn như onClick. Tuy nhiên, bạn cũng có thể tạo các component của riêng mình và cung cấp cho các đạo cụ trình xử lý sự kiện của chúng bất kỳ tên dành riêng cho ứng dụng nào mà bạn thích.

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Đang phát!')}
      onUploadImage={() => alert('Đang tải lên!')}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Phát phim
      </Button>
      <Button onClick={onUploadImage}>
        Tải ảnh lên
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

Ready to learn this topic?

Đọc Phản hồi các sự kiện để tìm hiểu cách thêm trình xử lý sự kiện.

Read More

State: bộ nhớ của một component

Các component thường cần thay đổi những gì trên màn hình do kết quả của một tương tác. Nhập vào biểu mẫu sẽ cập nhật trường nhập liệu, nhấp vào “tiếp theo” trên băng chuyền hình ảnh sẽ thay đổi hình ảnh nào được hiển thị, nhấp vào “mua” sẽ đưa một sản phẩm vào giỏ hàng. Các component cần “ghi nhớ” mọi thứ: giá trị đầu vào hiện tại, hình ảnh hiện tại, giỏ hàng. Trong React, loại bộ nhớ dành riêng cho component này được gọi là state.

Bạn có thể thêm state vào một component bằng Hook useState. Hook là các hàm đặc biệt cho phép các component của bạn sử dụng các tính năng của React (state là một trong những tính năng đó). Hook useState cho phép bạn khai báo một biến state. Nó lấy state ban đầu và trả về một cặp giá trị: state hiện tại và một hàm setter state cho phép bạn cập nhật nó.

const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);

Đây là cách một thư viện ảnh sử dụng và cập nhật state khi nhấp:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  const hasNext = index < sculptureList.length - 1;

  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    } else {
      setIndex(0);
    }
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Tiếp theo
      </button>
      <h2>
        <i>{sculpture.name} </i>
        bởi {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} trên {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Ẩn' : 'Hiện'} chi tiết
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Ready to learn this topic?

Đọc State: Bộ nhớ của một component để tìm hiểu cách ghi nhớ một giá trị và cập nhật nó khi tương tác.

Read More

Render và commit

Trước khi các component của bạn được hiển thị trên màn hình, chúng phải được render bởi React. Hiểu các bước trong quy trình này sẽ giúp bạn suy nghĩ về cách mã của bạn thực thi và giải thích hành vi của nó.

Hãy tưởng tượng rằng các component của bạn là những đầu bếp trong bếp, lắp ráp các món ăn ngon từ các nguyên liệu. Trong kịch bản này, React là người phục vụ đưa các yêu cầu từ khách hàng và mang chúng đến cho họ. Quá trình yêu cầu và phục vụ UI này có ba bước:

  1. Kích hoạt render (giao đơn đặt hàng của khách ăn tối cho nhà bếp)
  2. Rendering component (chuẩn bị đơn hàng trong bếp)
  3. Committing vào DOM (đặt hàng lên bàn)
  1. React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen.
    Kích hoạt
  2. The Card Chef gives React a fresh Card component.
    Render
  3. React delivers the Card to the user at their table.
    Commit

Illustrated by Rachel Lee Nabors

Ready to learn this topic?

Đọc Render và Commit để tìm hiểu vòng đời của một bản cập nhật UI.

Read More

State như một snapshot

Không giống như các biến JavaScript thông thường, state của React hoạt động giống như một snapshot hơn. Đặt nó không thay đổi biến state bạn đã có, mà thay vào đó kích hoạt một lần render lại. Điều này có thể gây ngạc nhiên lúc đầu!

console.log(count); // 0
setCount(count + 1); // Yêu cầu render lại với 1
console.log(count); // Vẫn là 0!

Hành vi này giúp bạn tránh các lỗi tinh vi. Đây là một ứng dụng trò chuyện nhỏ. Hãy thử đoán điều gì xảy ra nếu bạn nhấn “Gửi” trước và sau đó thay đổi người nhận thành Bob. Tên của ai sẽ xuất hiện trong alert năm giây sau đó?

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Hello');

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`Bạn đã nói ${message} với ${to}`);
    }, 5000);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Đến:{' '}
        <select
          value={to}
          onChange={e => setTo(e.target.value)}>
          <option value="Alice">Alice</option>
          <option value="Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder="Tin nhắn"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Gửi</button>
    </form>
  );
}

Ready to learn this topic?

Đọc State như một Snapshot để tìm hiểu lý do tại sao state xuất hiện “cố định” và không thay đổi bên trong các trình xử lý sự kiện.

Read More

Xếp hàng đợi một loạt các bản cập nhật state

Component này bị lỗi: nhấp vào “+3” chỉ tăng điểm một lần.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(score + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Điểm: {score}</h1>
    </>
  )
}

State như một Snapshot giải thích tại sao điều này xảy ra. Đặt state yêu cầu một lần render lại mới, nhưng không thay đổi nó trong mã đã chạy. Vì vậy, score tiếp tục là 0 ngay sau khi bạn gọi setScore(score + 1).

console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0

Bạn có thể khắc phục điều này bằng cách truyền một hàm cập nhật khi đặt state. Lưu ý cách thay thế setScore(score + 1) bằng setScore(s => s + 1) sẽ sửa nút “+3”. Điều này cho phép bạn xếp hàng đợi nhiều bản cập nhật state.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(s => s + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Điểm: {score}</h1>
    </>
  )
}

Ready to learn this topic?

Đọc Xếp hàng đợi một loạt các bản cập nhật state để tìm hiểu cách xếp hàng đợi một chuỗi các bản cập nhật state.

Read More

Cập nhật các đối tượng trong state

State có thể giữ bất kỳ loại giá trị JavaScript nào, bao gồm cả đối tượng. Nhưng bạn không nên thay đổi trực tiếp các đối tượng và mảng mà bạn giữ trong state của React. Thay vào đó, khi bạn muốn cập nhật một đối tượng và mảng, bạn cần tạo một đối tượng mới (hoặc tạo một bản sao của một đối tượng hiện có), sau đó cập nhật state để sử dụng bản sao đó.

Thông thường, bạn sẽ sử dụng cú pháp spread ... để sao chép các đối tượng và mảng mà bạn muốn thay đổi. Ví dụ: cập nhật một đối tượng lồng nhau có thể trông như thế này:

import { useState } from 'react';

export default function Form() {
  const [person, setPerson] = useState({
    name: 'Niki de Saint Phalle',
    artwork: {
      title: 'Blue Nana',
      city: 'Hamburg',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });

  function handleNameChange(e) {
    setPerson({
      ...person,
      name: e.target.value
    });
  }

  function handleTitleChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        title: e.target.value
      }
    });
  }

  function handleCityChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        city: e.target.value
      }
    });
  }

  function handleImageChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        image: e.target.value
      }
    });
  }

  return (
    <>
      <label>
        Tên:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <label>
        Tiêu đề:
        <input
          value={person.artwork.title}
          onChange={handleTitleChange}
        />
      </label>
      <label>
        Thành phố:
        <input
          value={person.artwork.city}
          onChange={handleCityChange}
        />
      </label>
      <label>
        Hình ảnh:
        <input
          value={person.artwork.image}
          onChange={handleImageChange}
        />
      </label>
      <p>
        <i>{person.artwork.title}</i>
        {' bởi '}
        {person.name}
        <br />
        (tọa lạc tại {person.artwork.city})
      </p>
      <img
        src={person.artwork.image}
        alt={person.artwork.title}
      />
    </>
  );
}

Nếu việc sao chép các đối tượng trong mã trở nên tẻ nhạt, bạn có thể sử dụng một thư viện như Immer để giảm mã lặp đi lặp lại:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Đọc Cập nhật các đối tượng trong state để tìm hiểu cách cập nhật các đối tượng một cách chính xác.

Read More

Cập nhật các mảng trong state

Mảng là một loại đối tượng JavaScript có thể thay đổi khác mà bạn có thể lưu trữ trong state và nên coi là chỉ đọc. Giống như với các đối tượng, khi bạn muốn cập nhật một mảng được lưu trữ trong state, bạn cần tạo một mảng mới (hoặc tạo một bản sao của một mảng hiện có), sau đó đặt state để sử dụng mảng mới:

import { useState } from 'react';

const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [list, setList] = useState(
    initialList
  );

  function handleToggle(artworkId, nextSeen) {
    setList(list.map(artwork => {
      if (artwork.id === artworkId) {
        return { ...artwork, seen: nextSeen };
      } else {
        return artwork;
      }
    }));
  }

  return (
    <>
      <h1>Danh sách việc nên làm nghệ thuật</h1>
      <h2>Danh sách nghệ thuật của tôi để xem:</h2>
      <ItemList
        artworks={list}
        onToggle={handleToggle} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

Nếu việc sao chép các mảng trong mã trở nên tẻ nhạt, bạn có thể sử dụng một thư viện như Immer để giảm mã lặp đi lặp lại:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Đọc Cập nhật các mảng trong state để tìm hiểu cách cập nhật các mảng một cách chính xác.

Read More

Tiếp theo là gì?

Đi tới Phản hồi các sự kiện để bắt đầu đọc trang chương này theo từng trang!

Hoặc, nếu bạn đã quen thuộc với các chủ đề này, tại sao không đọc về Quản lý state?