React: My Experience
Đợt vừa rồi lại làm dự án với React, note lại một số “trải nghiệm” hữu ích thêm ^^
Performance
Việc sử dụng state không đúng có thể dẫn đến component render rất nhiều lần. Một số Component “nặng” render cũng mất thời gian và công việc của bạn là cần tìm một công cụ để biết các component này, sau đó tối ưu
Profiler
React cũng cấp Profiler API cho phép bạn xem các lần render và “chi phí” (thời gian) cho mỗi lần render. Để sử dụng Profiler, bạn có thể xem trên DevTools hoặc thực hiện Coding
1 | import React, { Profiler } from "react"; |
Gộp State
Khi dùng nhiều biến State, bạn có thể gộp chúng lại để giảm thiểu số lần render. Ví dụ thay vì nhiều state1
2
3
4
5function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
Bạn có thể gộp cung chung lại thành một state1
2
3
4
5
6
7function ExampleWithManyStates() {
// Declare multiple state variables!
const [manyState, setManyState] = useState({
age: 42,
fruit: 'banana',
todos: { text: 'Learn Hooks' }]
});
Tuy nhiên một khi đã gộp state, mỗi lần cập nhật state, bạn phải merge các giá trị trước đó nữa một cách thủ công1
2
3
4
5
6
7
8
9
10
11// ...
useEffect(() => {
function handleWindowMouseMove(e) {
// "...state" để đảm bảo không "mất" giá trị width và height
setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
}
// Lưu ý: phần này viết đơn giản nhất có thể
window.addEventListener('mousemove', handleWindowMouseMove);
return () => window.removeEventListener('mousemove', handleWindowMouseMove);
}, []);
// ...
Cách merge toàn bộ state về một object chưa phải là tối ưu hẳn. Bạn có thể sử dụng đan xem việc tách nhiều state và gộp lại với nhau trong một Component để hài hoà. Chúng ta chỉ nên gộp state nếu các state có logic liên quan đến nhau.
Chi tiết xem thêm tại Một số câu hỏi thường gặp với Hook.
Memo, useCallback
React memo là một HOC giúp tối ưu các việc render không cần thiết
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
Code-Splitting
Đây là cách tối ưu source code đi liền với việc cấu trúc thư mục của bạn. Một hướng dẫn hữu ích bạn nên đọc qua.
Đóng Gói (Bundling)
Cách đóng gói code, giảm các tab và space khi deploy trên môi trường production
Code Splitting
Đóng gói hẵn rất tuyệt vời, nhưng khi ứng dụng của bạn trở nên lớn hơn, file đóng gói của bạn cũng sẽ lớn theo. Đặc biệt khi bạn sử dụng third-party library (thư viện bên thứ 3) lớn. Bạn cần phải cẩn thận với những đoạn code bạn đang include vào bundle của mình, bằng cách đó bạn sẽ không vô tình làm nó trở nên quá lớn khiến ứng dụng mất nhiều thời gian để tải.
Để tránh việc nhận được một bundle lớn, tốt nhất nên bắt đầu “splitting (chia nhỏ)” gói bundle của bạn. Code-Splitting là một feature hỗ trợ bởi bundler như Webpack, Rollup và Browserify (via factor-bundle) nó có thể tạo ra nhiều bundle nhỏ có thể được load một cách tự động tại thời điểm runtime.
Phân chia code cho ứng dụng giúp “lazy-load” chỉ những phần người dùng đang cần, tăng đáng kể hiệu suất mà không cần phải giảm số lượng code trong ứng dụng, bạn đã tránh phải tải những đoạn code người dùng có thể sẽ không bao giờ cần đến, và giảm số lượng code cần tải lên trong lần đầu tiên.
Đoạn này hiểu là chia code thành các folder và import các file, component thực sự cần thiết :D
import()
Phương pháp tốt nhất để sử dụng code-splitting trong ứng dụng là thông qua cú pháp import() động.
Trước1
2
3import { add } from './math';
console.log(add(16, 26));
Sau1
2
3import("./math").then(math => {
console.log(math.add(16, 26));
});
React.lazy
Chú ý:
React.lazy và Suspense chưa có sẵn cho server-side rendering. Nếu bạn muốn phân chia code ở những ứng dụng render tại server, chúng tôi xin giới thiệu Loadable Components. Nó có hướng dẫn phân chia code với server-side rendering.
Error boundaries
Nếu OtherComponent không thể tải lên (Ví dụ, lỗi mạng), nó sẽ kích hoạt lỗi. Bạn có thể điều khiển những lỗi đó để hiển thị một trải nghiệm người dùng tốt hơn và quản lý phục hồi với Error Boundaries. Một khi bạn đã tạo Error Boundary, bạn có thể sử dụng nó bất kỳ nơi nào bên trên lazy components của bạn để hiển thị thông báo lỗi khi có sự cố về mạng.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);
Performance Tab
https://reactjs.org/docs/optimizing-performance.html
Theme + Global Style
- https://mui.com/material-ui/customization/theming/
- https://stackoverflow.com/questions/58755118/global-styles-with-react-and-mui
Lỗi import vòng
Nếu bạn bị trang trắng và các lỗi liên quan đến gọi phương thức của một object null (Uncaught TypeError), có thể bạn đang bị lỗi này1
Uncaught TypeError: Super expression must either be null or a function, not undefined
Hãy theo dõi trace của console để tìm ra chỗ bị import vòng
Debug Tool
Cách share data
- Redux
- Context
Others
Render Props
ref ,forwardRef
Config env
Để thêm biến env cho React, bạn chỉ cần tạo file .env
và đặt tên biến với tiền tố REACT_APP_
1
2#.env
REACT_APP_NAME="Ming"
và sử dụng1
console.log(process.env.REACT_APP_NAME)
Ngoài ra thứ tự load file env có thể xem tại đây
Proxy
Sử dụng khi điều hướng qua Proxy
- https://create-react-app.dev/docs/proxying-api-requests-in-development/
- Package: http-proxy-middleware
QA (cần đọc hết)
- Component State: https://vi.reactjs.org/docs/faq-state.html
- Một số câu hỏi thường gặp với Hook: https://vi.reactjs.org/docs/hooks-faq.html
Portals
Cánh cổng giúp “dịch chuyển” thẻ con đến ngoài thẻ cha
Uncontrolled Components
Trong hầu hết các trường hợp, chúng tôi khuyên bạn nên sử dụng controlled components để triển khai forms. Trong controlled component, dữ liệu trong form sẽ được quản lí hoàn toàn bởi React component. Trái ngược với điều đó, uncontrolled component, dữ liệu sẽ được quản lí trực tiếp bởi chính DOM.
Để tạo uncontrolled component, thay vì việc xử lí sự kiện mỗi khi state được update, bạn có thể sử dụng ref để lấy dữ liệu từ DOM.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Vì uncontrolled component chỉ dựa trên DOM, điều này khiến việc tương tác với code React và non-React khá dễ dàng. Nếu bạn muốn code nhanh và bẩn, uncontrolled components có thể giúp bạn code ít code hơn. Nếu không, bạn nên sử dụng controlled components.
React: My Experience