From 4e2f81c224cf2d7e991201e14bd521cfef2c1405 Mon Sep 17 00:00:00 2001 From: spritelytrung Date: Thu, 4 Nov 2021 15:48:45 +0700 Subject: [PATCH 1/3] 09-function --- book/09-function-component-breakdown.md | 221 ++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 book/09-function-component-breakdown.md diff --git a/book/09-function-component-breakdown.md b/book/09-function-component-breakdown.md new file mode 100644 index 0000000..58ba18d --- /dev/null +++ b/book/09-function-component-breakdown.md @@ -0,0 +1,221 @@ +# 09.01: Khi nào nên tách function ra riêng + +# 09.02: Khi nào nên tách component + +## Table of contents + +- [08: Http request in React (bonus: using Axios)](<#08-Httprequest-in-React-(bonus-using-Axios)>) + - [1. API là gì?](#1-API-là-gì) + - [2. Call API by axios](#2-Call-API-by-axios) + - [2.1 Get request](#21-Get-request) + - [2.2 Post Requests](#22-Post-request) + - [2.3 Delete Requests](#33-Delete-request) + - [3. Tổ chức API module như thế nào?](#3-Tổ-chức-API-module-như-thế-nào?) + +--- + +## 1. Mục đích của việc tách component + +Khi dự án mở rộng hơn hoặc đơn giản chỉ là hiện tại đang có nhiều component tương tự nhau bị lặp lại. Thì người ta sẽ tìm cách tách các component ra để có thể tái sử dụng chúng. + +Để dễ trình bày mình sẽ đặt tên chung cho các component được tái sử dụng là component A + +Ưu điểm: + +- Đảm bảo tính đồng bộ cho code vì các component A được tái sử dụng, dẫn đến hạn chế các sai sót +- Khi có sự thay đổi về giao diện hoặc chức năng mà có liên quan đến việc sử dụng component A thì sẽ chỉ phải sửa lại component A hoặc nếu có thể thì chỉ là mở rộng trong nội bộ component A + Nhược điểm: +- Việc tách component A ra riêng là con dao hai lưỡi. Vì phải hoạch định được chính xác các component nào có thể tái sử dụng để đưa ra riêng cũng như thiết lấp logic bên trong component đủ tốt để có thể dễ dàng trong việc sử dụng. + +## 2. Các ví dụ điển hình khi nào nên tách component để tái sử dụng + +### 2.1 Wrap Component, Layout Component hoặc Template Component + +#### 2.1a Component chỉ nhận props children + +Đây là các component cực kỳ đơn giản và dễ sử dụng, chúng chỉ nhận vào props children là các element + +```jsx +import React from "react"; +import { Box, Typography } from "@material-ui/core"; + +const cssContainer = { + width: "100%", + borderRadius: "50px" + margin: "15px 25px", +} + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children }) => { + return ( + + {children} + + ) +} + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header + + + ) +} +``` + +#### 2.1b Component nhận props và có sãn các component khác + +Đây là các component không có hoặc có sẵn các component gốc từ trước và nhận thêm props children là các element + +```jsx +import React from "react"; +import { Box, Button } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 25px", + display: "flex", + justifyContent: "space-between", +}; + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children, link }) => { + const history = useHistory(); + function routerPrevPage(link) { + history.push(link); + } + return ( + + {children} + {/* Button dùng để router về trang trước luôn có sẵn*/} + + + ); +}; + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header có button router + + + ); +}; +``` + +#### 2.1c Component nhận props nhưng props có liên quan đến logic bên trong và có sãn các component khác + +```jsx +import React from "react"; +import { Box, Button } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 25px", + display: "flex", + justifyContent: "space-between", +}; + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children, link, search }) => { + const history = useHistory(); + function routerPrevPage(link) { + history.push(link); + } + return ( + + {children} + {/* Button dùng để router về trang trước luôn có sẵn*/} + {search && } + {/* Button dùng để router về trang trước luôn có sẵn*/} + {link && } + + ); +}; + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header có button router, search input + + + ); +}; +``` +### 2.2 Render props + + +```jsx +import React from "react"; +import { Box, Button } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; +import AddIcon from '@material-ui/icons/Add'; +import ClearIcon from '@material-ui/icons/Clear'; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 0", + display: "flex", + justifyContent: "space-between", +}; + +const passedUsers = [ + "Thai", "Huy", "Phong", "Hieu" +] +const rejectedUsers = [ + "Hien", "Long" +] + +const List = ({ data, render }) => data.map((user, index) => render(user)) +const WrapListItem = ({children}) => {children} +// Sử dụng thực tế +const App = () => { + return ( + + + + + + {user.name} + + + } + /> + + + + + {user.name} + + + { + ["intern", "fresher", "middle", "senior"].map((option,index) => + + {option} + + ) + } + + + } + /> + + ); +}; +``` \ No newline at end of file From b33c7f742f60336ec5530caed5b6129443beae71 Mon Sep 17 00:00:00 2001 From: spritelytrung Date: Fri, 12 Nov 2021 15:51:06 +0700 Subject: [PATCH 2/3] new docs function, component --- book/09-function-component-breakdown.md | 533 ++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 book/09-function-component-breakdown.md diff --git a/book/09-function-component-breakdown.md b/book/09-function-component-breakdown.md new file mode 100644 index 0000000..6ac0ea3 --- /dev/null +++ b/book/09-function-component-breakdown.md @@ -0,0 +1,533 @@ +# 09.01: Khi nào nên tách function ra riêng + +## Table of contents + +- [09: Function-Breakdown ](#09-Function,Component-Breakdown) + - [1. Mục đích của việc tách component ](#1-Mục-đích-của-việc-tách-component) + - [2. Ví dụ của việc tách function, custom-hook ](#1-Ví-dụ-của-việc-tách-function-custom-hook) + - [2.1 useDialog](#21-useDialog) + - [2.2 usePropsForm](#22-usePropsForm) + +--- + +## 1. Mục đích của việc tách function + +Khi dự án có nhiều function bị lặp lại quá nhiều thì đây là lúc thích hợp để tách function đó ra riêng để tái sử dụng. Custom hook cũng là một trong số đó. + +## 2. Ví dụ của việc tách function, custom-hook + +### 2.1 useDialog + +Dialog được sử dụng rất nhiều trong các dự án từ nhỏ tới lớn thay vì việc mỗi khi ta muốn dùng một dialog thì phải khai báo một state, function để quản lý trạng thái của dialog. Do đó ta có thể tạo một custom hook để gói gọn những thứ trên vào trong một function duy nhất + +```jsx +import { Box, Button, Dialog } from "@material-ui/core"; +import React from "react"; +import ReactDOM from "react-dom"; + +const Modal = ({ isOpen, onClose }) => isOpen ? ReactDOM.createPortal( + + + + This is Dialog + + , document.body +) : null +export default Modal + +import { Button, Box} from '@material-ui/core' +function App() { + const { isOpen, close, toggle } = useDialog() + return ( +
+ + +
+ ) +} +export default App +``` + +Như trên ta có thể thấy hook useDialog cung cấp cho component các state, function để tương tác với dialog thay vì việc phải khởi tạo những thứ đó từ đầu như trước đây. + +Ngoài ra có thể truyền các props children, function vào Modal để tăng tính linh +hoạt cho Modal. + +### 2.2 usePropsForm + +Khi sử dụng các thư viện như formik, react-hook-form sẽ có những đoạn hardcode bị lặp lại liên tục như sau: + +Với formik : + +```jsx +import React from "react"; +import { useFormik } from "formik"; + +const App = () => { + const formik = useFormik({ + initialValues: { + firstName: "", + lastName: "", + email: "", + }, + onSubmit: (values) => { + alert(JSON.stringify(values, null, 2)); + }, + }); + return ( + <> +
+ + + + + + + +
+ + ); +}; +``` + +Ta có thể thấy ở trên có rất nhiều props bị lặp lại dẫn tới việc phải hardcode quá nhiều và tăng số lượng dòng code lên. Đây là lúc ta có thể sử dụng một function để trả về các props như trên + +```jsx +import React from "react"; +import { useFormik } from 'formik'; + +const usePropsForm = (name) => { + return { + id: name, + name: name, + type: "text", + onChange: {formik.handleChange}, + value={formik.values.name} + } +} +const App = () => { + const formik = useFormik({ + initialValues: { + firstName: '', + lastName: '', + email: '', + }, + onSubmit: values => { + alert(JSON.stringify(values, null, 2)); + }, + }); + return ( + <> +
+ + + + + + + +
+ + ) +} +``` + +Code đã ngắn gọn hơn rất nhiều + +# 09.02: Khi nào nên tách component + +## Table of contents + +- [09: Function,Component-Breakdown ](#09-Function,Component-Breakdown) + - [1. Mục đích của việc tách component ](#1-Mục-đích-của-việc-tách-component) + - [2. Các ví dụ điển hình khi nào nên tách component để tái sử dụng](#2-Các-ví-dụ-điển-hình-tách-component) + - [2.1 Wrap Component, Layout Component hoặc Template Component](#21-Wrap-Component-Layout-Component-Template-Component) -[2.1a Component chỉ nhận props children](#21a-Component-chỉ-nhận-props-children) -[2.1b Component nhận props và có sãn các component khác](#21b-Component-nhận-props-và-có-sẵn-các-component-khác) -[2.1c Component nhận props nhưng props có liên quan đến logic bên trong và có sãn các component khác](#21b-Component-nhận-props-nhưng-props-liên-quan-đến-logic-bên-trong-và-có-sẵn-các-component-khác) + +--- + +## 1. Mục đích của việc tách component + +Khi dự án mở rộng hơn hoặc đơn giản chỉ là hiện tại đang có nhiều component tương tự nhau bị lặp lại. Người ta sẽ tìm cách tách các component ra để có thể tái sử dụng chúng. +Để dễ trình bày mình sẽ đặt tên chung cho các component được tái sử dụng là component A + +Ưu điểm: + +- Đảm bảo tính đồng bộ cho code vì các component A được tái sử dụng, dẫn đến hạn chế các sai sót +- Khi có sự thay đổi về giao diện hoặc chức năng mà có liên quan đến việc sử dụng component A thì sẽ chỉ phải sửa lại component A hoặc nếu có thể thì chỉ là mở rộng trong nội bộ component A + +Nhược điểm: + +- Việc tách component A ra riêng là con dao hai lưỡi. Vì phải hoạch định được chính xác các component nào có thể tái sử dụng để đưa ra riêng cũng như thiết lấp logic bên trong component đủ tốt để có thể dễ dàng trong việc sử dụng. + +## 2. Các ví dụ điển hình khi nào nên tách component để tái sử dụng + +### 2.1 Wrap Component, Layout Component hoặc Template Component + +#### 2.1a Component chỉ nhận props children + +Đây là các component cực kỳ đơn giản và dễ sử dụng, chúng chỉ nhận vào props children là các element + +```jsx +import React from "react"; +import { Box, Typography } from "@material-ui/core"; + +const cssContainer = { + width: "100%", + borderRadius: "50px" + margin: "15px 25px", +} + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children }) => { + return ( + + {children} + + ) +} + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header + + + ) +} +``` + +#### 2.1b Component nhận props và có sãn các component khác + +Đây là các component có sẵn các component gốc từ trước và nhận thêm props children là các element + +```jsx +import React from "react"; +import { Box, Button } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 25px", + display: "flex", + justifyContent: "space-between", +}; + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children, link }) => { + const history = useHistory(); + function routerPrevPage(link) { + history.push(link); + } + return ( + + {children} + + + ); +}; + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header có button router + + + ); +}; +``` + +#### 2.1c Component nhận props nhưng props có liên quan đến logic bên trong và có sãn các component khác + +Đây là các component không có hoặc có sẵn các component gốc từ trước và nhận thêm props để xử lý logic bên trong component + +```jsx +import React from "react"; +import { Box, Button } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 25px", + display: "flex", + justifyContent: "space-between", +}; + +// Component Header sẽ mặc định có các thuộc tính css của cssContainer +const HeaderTemplate = ({ children, link, search }) => { + const history = useHistory(); + function routerPrevPage(link) { + history.push(link); + } + return ( + + {children} + {search && } + {link && } + + ); +}; + +// Sử dụng thực tế +const App = () => { + return ( + + + Đây là Header có button router, search input + + + ); +}; +``` + +### 2.2 Render props + +Đây là một phương pháp có mục đích tương tự với phương pháp sử dụng Higher Order Component, giúp chúng ta sử dụng lại logic trên nhiều component + +```jsx +import React from "react"; +import { Box, Button, Typography, MenuItem } from "@material-ui/core"; +import { useHistory } from "react-router-dom"; +import AddIcon from '@material-ui/icons/Add'; +import ClearIcon from '@material-ui/icons/Clear'; + +const cssContainer = { + width: "100%", + boxSizing: "border-box", + margin: "15px 0", + display: "flex", + justifyContent: "space-between", +}; + +const avaiableUsers = [ + "Thai", "Huy", "Phong", "Hieu" +] +const selectedUsers = [ + "Hien", "Long" +] + +const List = ({ data, render }) => data.map((user, index) => render(user)) +const WrapListItem = ({children}) => {children} +// Sử dụng thực tế +const App = () => { + return ( + + {/* List chỉ có button clear*/} + + + + + ); +} +``` +## 2 Ví dụ về việc sử dụng useRef +### 2.1 Lưu giữ giá trị ban đầu +```jsx +import React from "react"; +import { Box, Dialog, Button } from "@material-ui/core"; +const selectedList = [ + { + name: "male", isChecked: true + }, + { + name: "female", isChecked: false + }, + { + name: "ageOver18", isChecked: false + }, +] + +const DialogFilter = ({open, onDialog, onSubmitList}) => { + const [ newSelectedListm setNewSelectedList ] = useState([]) + const prevSelected = useRef(selectedList) + const handleSelected = (e) => { + const index = selectedList.indexOf((item,index) => return item.name === e.target.name ) + setNewSelectedList(prevState => [ + ...prevState, + { + ...prevState[index], + isChecked: !prevState[index].isChecked + } + ]) + } + functon resetDefaultFilter() { + setNewSelectedList(prevSelected) + onDialog() + } + useEffect(() => { + setNewSelectedList(selectedList) + // clean up ref + return () => prevSelected.current = {} + },[]) + return ( + + + { + selectedList.map((item,index) => + + {item.name} + + + ) + } + + + + + ) +} +const App = () => { + const [ selectedValue, setSelectedValue ] = useState(selectedList) + const [ isOpen, setOpen ] = useState(false) + function handleDialog() { + setOpen(!isOpen) + } + fucntion submitFilter(list) { + setSelectedValue(list) + handleDialog() + } + return ( + <> + + + + ); +} +``` + +Docs vẫn đang trong quá trình hoàn thiện !!! From c62f6490c4e0719568ae8bc8413f7edf070af7bd Mon Sep 17 00:00:00 2001 From: Trung Do <91188457+spritelytrung@users.noreply.github.com> Date: Fri, 12 Nov 2021 16:07:54 +0700 Subject: [PATCH 3/3] Delete 09-function-component-breakdown.md --- book/09-function-component-breakdown.md | 221 ------------------------ 1 file changed, 221 deletions(-) delete mode 100644 book/09-function-component-breakdown.md diff --git a/book/09-function-component-breakdown.md b/book/09-function-component-breakdown.md deleted file mode 100644 index 58ba18d..0000000 --- a/book/09-function-component-breakdown.md +++ /dev/null @@ -1,221 +0,0 @@ -# 09.01: Khi nào nên tách function ra riêng - -# 09.02: Khi nào nên tách component - -## Table of contents - -- [08: Http request in React (bonus: using Axios)](<#08-Httprequest-in-React-(bonus-using-Axios)>) - - [1. API là gì?](#1-API-là-gì) - - [2. Call API by axios](#2-Call-API-by-axios) - - [2.1 Get request](#21-Get-request) - - [2.2 Post Requests](#22-Post-request) - - [2.3 Delete Requests](#33-Delete-request) - - [3. Tổ chức API module như thế nào?](#3-Tổ-chức-API-module-như-thế-nào?) - ---- - -## 1. Mục đích của việc tách component - -Khi dự án mở rộng hơn hoặc đơn giản chỉ là hiện tại đang có nhiều component tương tự nhau bị lặp lại. Thì người ta sẽ tìm cách tách các component ra để có thể tái sử dụng chúng. - -Để dễ trình bày mình sẽ đặt tên chung cho các component được tái sử dụng là component A - -Ưu điểm: - -- Đảm bảo tính đồng bộ cho code vì các component A được tái sử dụng, dẫn đến hạn chế các sai sót -- Khi có sự thay đổi về giao diện hoặc chức năng mà có liên quan đến việc sử dụng component A thì sẽ chỉ phải sửa lại component A hoặc nếu có thể thì chỉ là mở rộng trong nội bộ component A - Nhược điểm: -- Việc tách component A ra riêng là con dao hai lưỡi. Vì phải hoạch định được chính xác các component nào có thể tái sử dụng để đưa ra riêng cũng như thiết lấp logic bên trong component đủ tốt để có thể dễ dàng trong việc sử dụng. - -## 2. Các ví dụ điển hình khi nào nên tách component để tái sử dụng - -### 2.1 Wrap Component, Layout Component hoặc Template Component - -#### 2.1a Component chỉ nhận props children - -Đây là các component cực kỳ đơn giản và dễ sử dụng, chúng chỉ nhận vào props children là các element - -```jsx -import React from "react"; -import { Box, Typography } from "@material-ui/core"; - -const cssContainer = { - width: "100%", - borderRadius: "50px" - margin: "15px 25px", -} - -// Component Header sẽ mặc định có các thuộc tính css của cssContainer -const HeaderTemplate = ({ children }) => { - return ( - - {children} - - ) -} - -// Sử dụng thực tế -const App = () => { - return ( - - - Đây là Header - - - ) -} -``` - -#### 2.1b Component nhận props và có sãn các component khác - -Đây là các component không có hoặc có sẵn các component gốc từ trước và nhận thêm props children là các element - -```jsx -import React from "react"; -import { Box, Button } from "@material-ui/core"; -import { useHistory } from "react-router-dom"; - -const cssContainer = { - width: "100%", - boxSizing: "border-box", - margin: "15px 25px", - display: "flex", - justifyContent: "space-between", -}; - -// Component Header sẽ mặc định có các thuộc tính css của cssContainer -const HeaderTemplate = ({ children, link }) => { - const history = useHistory(); - function routerPrevPage(link) { - history.push(link); - } - return ( - - {children} - {/* Button dùng để router về trang trước luôn có sẵn*/} - - - ); -}; - -// Sử dụng thực tế -const App = () => { - return ( - - - Đây là Header có button router - - - ); -}; -``` - -#### 2.1c Component nhận props nhưng props có liên quan đến logic bên trong và có sãn các component khác - -```jsx -import React from "react"; -import { Box, Button } from "@material-ui/core"; -import { useHistory } from "react-router-dom"; - -const cssContainer = { - width: "100%", - boxSizing: "border-box", - margin: "15px 25px", - display: "flex", - justifyContent: "space-between", -}; - -// Component Header sẽ mặc định có các thuộc tính css của cssContainer -const HeaderTemplate = ({ children, link, search }) => { - const history = useHistory(); - function routerPrevPage(link) { - history.push(link); - } - return ( - - {children} - {/* Button dùng để router về trang trước luôn có sẵn*/} - {search && } - {/* Button dùng để router về trang trước luôn có sẵn*/} - {link && } - - ); -}; - -// Sử dụng thực tế -const App = () => { - return ( - - - Đây là Header có button router, search input - - - ); -}; -``` -### 2.2 Render props - - -```jsx -import React from "react"; -import { Box, Button } from "@material-ui/core"; -import { useHistory } from "react-router-dom"; -import AddIcon from '@material-ui/icons/Add'; -import ClearIcon from '@material-ui/icons/Clear'; - -const cssContainer = { - width: "100%", - boxSizing: "border-box", - margin: "15px 0", - display: "flex", - justifyContent: "space-between", -}; - -const passedUsers = [ - "Thai", "Huy", "Phong", "Hieu" -] -const rejectedUsers = [ - "Hien", "Long" -] - -const List = ({ data, render }) => data.map((user, index) => render(user)) -const WrapListItem = ({children}) => {children} -// Sử dụng thực tế -const App = () => { - return ( - - - - - - {user.name} - - - } - /> - - - - - {user.name} - - - { - ["intern", "fresher", "middle", "senior"].map((option,index) => - - {option} - - ) - } - - - } - /> - - ); -}; -``` \ No newline at end of file