Tác giả: Reed Barger
1. Trạng thái React – React state thực sự được cập nhật như thế nào?
Là một React devs, bạn biết rằng trạng thái đó có thể được tạo và cập nhật với useState
và useReducerhook
.
Nhưng điều gì sẽ xảy ra chính xác khi bạn cập nhật trạng thái của một thành phần bằng một trong số các hooks? Trạng thái được cập nhật ngay lập tức hay được thực hiện sau đó?
Tìm việc làm lập trình viên React
Hãy xem đoạn code sau, đây là một ứng dụng truy cập rất đơn giản. Như bạn mong đợi, bạn có thể nhấp vào nút và bộ đếm tăng lên 1.
import React from 'react';
export default function App() {
const [count, setCount] = React.useState(0)
function addOne() {
setCount(count + 1);
}
return (
<div>
<h1>Count: {count}</h1> {/* 1 (as we expect) */}
<button onClick={addOne}>+ 1</button>
</div>
);
}
Nhưng điều gì sẽ xảy ra nếu chúng ta cố gắng thêm một dòng bổ sung, dòng này cũng cập nhật số lượng của chúng ta? Khi bạn nhấp vào nút, số lượng hiển thị của chúng tôi sẽ tăng lên một hay hai?
import React from 'react';
export default function App() {
const [count, setCount] = React.useState(0)
function addOne() {
setCount(count + 1);
setCount(count + 1);
}
return (
<div>
<h1>Count: {count}</h1> {/* 1?! */}
<button onClick={addOne}>+ 1</button>
</div>
);
}
Nếu chạy đoạn code này, nó chỉ tăng lên một! Mặc dù đã cố gắng tăng số lượng lên một hai lần, với hai bản cập nhật trạng thái riêng biệt.
Tại sao bộ đếm này hiển thị 1, mặc dù trạng thái tăng rõ ràng 1 hai lần?
Lý do là khi React developing việc lên lịch thực hiện cập nhật trạng thái khi chúng ta cập nhật trạng thái lần đầu tiên. Bởi vì nó chỉ được lên lịch và không được thực hiện ngay lập tức (không đồng bộ), count
không được cập nhật trước khi chúng tôi cố gắng cập nhật nó lần thứ hai.
Nói cách khác, bởi vì cập nhật trạng thái được lên lịch, không được thực hiện ngay lập tức, lần thứ hai gọi setCount
, count
vẫn chỉ là 0
, không phải 1
.
Cách mà chúng ta có thể sửa lỗi này để cập nhật trạng thái một cách đáng tin cậy, mặc dù các cập nhật trạng thái là không đồng bộ, là sử dụng hàm bên trong có sẵn trong hàm useState setter
.
Điều này cho phép chúng ta lấy trạng thái trước đó và trả về giá trị mà chúng ta muốn nó ở trong phần thân của hàm bên trong. Khi sử dụng mẫu này, bạn sẽ thấy rằng nó tăng lên hai như mong muốn ban đầu
import React from 'react';
export default function App() {
const [count, setCount] = React.useState(0)
function addOne() {
setCount(prevCount => prevCount + 1); // 1
setCount(prevCount => prevCount + 1); // 2
}
return (
<div>
<h1>Count: {count}</h1>
<button onClick={addOne}>+ 1</button>
</div>
);
}
2. Sử dụng nhiều hiệu ứng để có kết quả tốt hơn
Khi sử dụng một hiệu ứng, hầu hết khi React developing, các developer sẽ chỉ sử dụng một useEffect
và cố gắng thực hiện nhiều hiệu ứng khác trong cùng một hàm hiệu ứng. Bạn có thể quan sát điều đó thông qua đoạn code dưới đây
import React from "react";
export default function App() {
const [posts, setPosts] = React.useState([]);
const [comments, setComments] = React.useState([]);
React.useEffect(() => {
// fetching post data
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((data) => setPosts(data));
// fetching comments data
fetch("https://jsonplaceholder.typicode.com/comments")
.then((res) => res.json())
.then((data) => setComments(data));
}, []);
return (
<div>
<PostsList posts={posts} />
<CommentsList comments={comments} />
</div>
);
}
Thay vì cố gắng nhồi nhét tất cả các hiệu ứng phụ vào các chi tiết của một hiệu ứng chính, cũng như có thể sử dụng một hook trạng thái nhiều lần, bạn có thể sử dụng nhiều hiệu ứng hơn. Việc này sẽ giúp React hooks mang lại so với việc sử dụng các phương thức vòng đời – lifecycle methods trong các thành phần lớp.
Ví dụ với hàm componentDidMount
, cần phải bao gồm bất kỳ hành động nào mà bạn muốn thực hiện sau khi các thành phần được gắn kết. Bạn không thể chia nhỏ các hiệu ứng phụ của mình thành nhiều phương thức – mỗi phương thức vòng đời trong các class có thể được sử dụng một lần và chỉ một lần.
Lợi ích chính của React hooks là có thể chia nhỏ code dựa trên những gì nó đang làm. Nó không chỉ có thể tách các hành động mà còn đang thực hiện sau khi kết xuất thành nhiều hiệu ứng mà còn có thể đồng định vị trạng thái của mình
import React from "react";
export default function App() {
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((data) => setPosts(data));
}, []);
const [comments, setComments] = React.useState([]);
React.useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/comments")
.then((res) => res.json())
.then((data) => setComments(data));
}, []);
return (
<div>
<PostsList posts={posts} />
<CommentsList comments={comments} />
</div>
);
}
3. Không tối ưu hóa các chức năng cập nhật trạng thái (useState, useReducer)
Một nhiệm vụ phổ biến bất cứ khi nào chúng ta truyền một hàm gọi lại từ thành phần mẹ sang thành phần con là ngăn nó được tạo lại, trừ khi các đối số của nó đã thay đổi.
Có thể thực hiện việc tối ưu hóa này với sự hỗ trợ của useCallback
hook.
useCallback được đặc biệt tạo ra cho các hàm gọi lại được chuyển cho các thành phần con, để đảm bảo rằng chúng không được tạo lại một cách không cần thiết, điều này dẫn đến việc ảnh hưởng đến hiệu suất đối với các thành phần bất cứ khi nào có kết xuất lại.
Điều này là do bất cứ khi nào parent component kết xuất lại, nó sẽ khiến tất cả các child components cũng hiển thị lại. Đây là nguyên nhân khiến các hàm callback được tạo lại trên mỗi lần hiển thị.
Tuy nhiên, nếu chúng ta đang sử dụng một hàm setter để cập nhật trạng thái mà chúng ta đã tạo bằng hook useState hoặc useReducer, chúng ta không cần kết thúc điều đó với useCallback.
Nói cách khác, không cần phải làm điều này:
import React from "react";
export default function App() {
const [text, setText] = React.useState("")
// Don't wrap setText in useCallback (it won't change as is)
const handleSetText = React.useCallback((event) => {
setText(event.target.value);
}, [])
return (
<form>
<Input text={text} handleSetText={handleSetText} />
<button type="submit">Submit</button>
</form>
);
}
function Input({ text, handleSetText }) {
return(
<input type="text" value={text} onChange={handleSetText} />
)
}
4. useRef hook có thể duy trì trạng thái qua các lần hiển thị
Là các React devs và khi thực hiện việc lập trình với React developing sẽ rất hữu ích khi có thể tham chiếu đến một phần tử React nhất định với sự trợ giúp của một ref. Chúng tôi tạo các ref trong React với sự trợ giúp của useRef
hook. Tuy nhiên, điều quan trọng cần lưu ý với useRef
là không chỉ hữu ích cho việc tham chiếu đến một phần tử DOM nhất định.
Có một số lợi ích nhất định để có thể lưu trữ và cập nhật các giá trị useRef. Nó cho phép lưu trữ một giá trị không có trong bộ nhớ mà sẽ không bị xóa khi hiển thị lại.
Nếu muốn theo dõi một giá trị qua các lần hiển thị với sự trợ giúp của một biến đơn giản, nó sẽ được khởi động lại mỗi khi thành phần hiển thị. Tuy nhiên, nếu bạn sử dụng một tham chiếu, giá trị được lưu trữ trong đó sẽ không đổi trong các lần hiển thị thành phần.
import React from "react";
export default function App() {
const [count, setCount] = React.useState(0);
const ref = React.useRef({ hasRendered: false });
React.useEffect(() => {
if (!ref.current.hasRendered) {
ref.current.hasRendered = true;
console.log("perform action only once!");
}
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
</div>
);
}
Bài viết gốc đăng tải tại freecodecamp.org
Có thể bạn quan tâm:
- Giải thích React Component Lifecycle
- Tạo hiệu ứng trong react với React Spring
- 5 sai lầm thường thấy khi viết react component
Xem thêm Việc làm React hấp dẫn trên TopDev