Thứ tư, 22/07/2020 | 00:00 GMT+7

Cách chia sẻ trạng thái qua các thành phần phản ứng với ngữ cảnh

Trong hướng dẫn này, bạn sẽ chia sẻ trạng thái trên nhiều thành phần bằng cách sử dụng ngữ cảnh React . React context là một giao diện để chia sẻ thông tin với cácthành phần khác mà không cần chuyển dữ liệu làm đạo cụ một cách rõ ràng. Điều này nghĩa là bạn có thể chia sẻ thông tin giữa thành phần mẹ và thành phần con lồng nhau sâu hoặc lưu trữ dữ liệu trên toàn trang web ở một nơi duy nhất và truy cập chúng ở bất kỳ đâu trong ứng dụng. Bạn thậm chí có thể cập nhật dữ liệu từ các thành phần lồng nhau bằng cách cung cấp các chức năng cập nhật cùng với dữ liệu.

React context đủ linh hoạt để sử dụng như một hệ thống quản lý trạng thái tập trung cho dự án của bạn hoặc bạn có thể mở rộng phạm vi nó đến các phần nhỏ hơn trong ứng dụng của bạn . Với ngữ cảnh, bạn có thể chia sẻ dữ liệu trên toàn ứng dụng mà không cần bất kỳ công cụ bổ sung nào của bên thứ ba và với một lượng nhỏ cấu hình. Điều này cung cấp một giải pháp thay thế trọng lượng nhẹ hơn cho các công cụ như Redux , có thể trợ giúp với các ứng dụng lớn hơn nhưng có thể yêu cầu quá nhiều cài đặt cho các dự án quy mô vừa.

Trong suốt hướng dẫn này, bạn sẽ sử dụng ngữ cảnh để xây dựng một ứng dụng sử dụng các tập dữ liệu chung trên các thành phần khác nhau. Để minh họa điều này, bạn sẽ tạo một trang web nơi user có thể tạo các món salad tùy chỉnh. Trang web sẽ sử dụng ngữ cảnh để lưu trữ thông tin khách hàng, món yêu thích và món salad tùy chỉnh. Sau đó, bạn sẽ truy cập vào dữ liệu đó và cập nhật nó trong toàn bộ ứng dụng mà không cần chuyển dữ liệu qua đạo cụ. Đến cuối hướng dẫn này, bạn sẽ học cách sử dụng ngữ cảnh để lưu trữ dữ liệu ở các cấp độ khác nhau của dự án và cách truy cập và cập nhật dữ liệu trong các thành phần lồng nhau.

Yêu cầu

Bước 1 - Xây dựng cơ sở cho ứng dụng của bạn

Trong bước này, bạn sẽ xây dựng cấu trúc chung của trình tạo salad tùy chỉnh của bạn . Bạn sẽ tạo các thành phần để hiển thị các lớp phủ có thể có, danh sách các lớp phủ đã chọn và thông tin khách hàng.Khi bạn xây dựng ứng dụng với dữ liệu tĩnh, bạn sẽ thấy cách các phần thông tin khác nhau được sử dụng trong nhiều thành phần khác nhau và cách xác định các phần dữ liệu sẽ hữu ích trong ngữ cảnh.

Đây là một ví dụ về ứng dụng bạn sẽ xây dựng:

Trang web xây dựng salad

Lưu ý cách có thông tin mà bạn có thể cần sử dụng trên các thành phần. Ví dụ: tên user (đối với mẫu này là Kwame ) hiển thị dữ liệu user trong khu vực chuyển , nhưng bạn cũng có thể cần thông tin user để xác định các mục yêu thích hoặc cho trang thanh toán. Thông tin user cần phải được truy cập bởi bất kỳ thành phần nào trong ứng dụng. Nhìn vào chính công cụ tạo salad, mỗi thành phần salad cần có thể cập nhật danh sách Món salad của bạn ở cuối màn hình, vì vậy bạn cần lưu trữ và cập nhật dữ liệu đó từ một vị trí có thể truy cập được cho từng thành phần .

Bắt đầu bằng cách mã hóa cứng tất cả dữ liệu để bạn có thể tìm ra cấu trúc ứng dụng của bạn . Sau đó, bạn sẽ thêm ngữ cảnh bắt đầu trong bước tiếp theo. Ngữ cảnh cung cấp nhiều giá trị nhất khi các ứng dụng bắt đầu phát triển, vì vậy trong bước này, bạn sẽ xây dựng một số thành phần để hiển thị cách ngữ cảnh hoạt động trên cây thành phần. Đối với các thành phần hoặc thư viện nhỏ hơn, bạn thường có thể sử dụng các thành phần gói và kỹ thuật quản lý trạng thái cấp thấp hơn, như React Hooksquản lý dựa trên lớp .

Vì bạn đang xây dựng một ứng dụng nhỏ với nhiều thành phần, hãy cài đặt JSS đảm bảo rằng sẽ không có bất kỳ xung đột tên lớp nào và để bạn có thể thêm các kiểu trong cùng một file như một thành phần. Để biết thêm về JSS, hãy xem Cấu phần phản ứng tạo kiểu .

Chạy lệnh sau:

  • npm install react-jss

npm sẽ cài đặt thành phần và khi nó hoàn thành, bạn sẽ thấy một thông báo như sau:

Output
+ react-jss@10.3.0 added 27 packages from 10 contributors, removed 10 packages andaudited 1973 packages in 15.507s

Đến đây bạn đã cài đặt JSS, hãy xem xét các thành phần khác nhau mà bạn cần . Ở đầu trang, bạn sẽ có thành phần Navigation để lưu thông báo chào mừng. Thành phần tiếp theo sẽ là chính SaladMaker . Điều này sẽ giữ tiêu đề cùng với trình tạo và danh sách Salad của bạn ở dưới cùng. Phần có các thành phần sẽ là một thành phần riêng biệt được gọi là SaladBuilder , được lồng bên trong SaladMaker . Mỗi thành phần sẽ là một thể hiện của thành phần SaladItem . Cuối cùng, danh sách dưới cùng sẽ là một thành phần có tên là SaladSummary .

Lưu ý: Các thành phần không cần phải được chia theo cách này. Khi bạn làm việc trên các ứng dụng của bạn , cấu trúc của bạn sẽ thay đổi và phát triển khi bạn thêm nhiều chức năng hơn. Ví dụ này nhằm cung cấp cho bạn một cấu trúc để khám phá cách ngữ cảnh ảnh hưởng đến các thành phần khác nhau trong cây.

Đến đây bạn đã có ý tưởng về các thành phần bạn cần , hãy tạo một folder cho từng thành phần:

  • mkdir src/components/Navigation
  • mkdir src/components/SaladMaker
  • mkdir src/components/SaladItem
  • mkdir src/components/SaladBuilder
  • mkdir src/components/SaladSummary

Tiếp theo, xây dựng các thành phần từ trên xuống bắt đầu với Navigation . Đầu tiên, hãy mở file thành phần trong editor :

  • nano src/components/Navigation/Navigation.js

Tạo một thành phần được gọi là Navigation và thêm một số kiểu để tạo cho Navigation một đường viền và phần đệm:

[state-context-tutorial/src/components/Navigation/Navigation.js] import React from 'react'; import { createUseStyles } from 'react-jss';  const useStyles = createUseStyles({   wrapper: {     borderBottom: 'black solid 1px',     padding: [15, 10],     textAlign: 'right',   } });  export default function Navigation() {   const classes = useStyles();   return(     <div className={classes.wrapper}>       Welcome, Kwame     </div>   ) } 

Vì bạn đang sử dụng JSS, bạn có thể tạo các đối tượng kiểu trực tiếp trong thành phần thay vì file CSS. Shell bọc div sẽ có một phần đệm, một đường viền black solid và căn chỉnh văn bản ở bên phải bằng textAlign .

Lưu và đóng file . Tiếp theo, mở App.js , là folder root của dự án:

  • nano src/components/App/App.js

Nhập thành phần Navigation và hiển thị nó bên trong các thẻ trống bằng cách thêm các dòng được đánh dấu:

state-context-tutorial / src / components / App / App.js
import React from 'react'; import Navigation from '../Navigation/Navigation';  function App() {   return (     <>       <Navigation />     </>   ); }  export default App; 

Lưu và đóng file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và bạn sẽ thấy thanh chuyển :

Thanh  chuyển

Hãy coi thanh chuyển như một thành phần chung , vì trong ví dụ này, nó đóng role như một thành phần mẫu sẽ được sử dụng lại trên mọi trang.

Thành phần tiếp theo sẽ là chính SaladMaker . Đây là một thành phần sẽ chỉ hiển thị trên một số trang nhất định hoặc ở một số trạng thái nhất định.

Mở SaladMaker.js trong editor của bạn:

  • nano src/components/SaladMaker/SaladMaker.js

Tạo một thành phần có <h1> với tiêu đề:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react'; import { createUseStyles } from 'react-jss';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export default function SaladMaker() {   const classes = useStyles();   return(     <>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>     </>   ) } 

Trong đoạn mã này, bạn đang sử dụng textAlign để căn giữa thành phần trên trang. Các thuộc tính rolearia-label của phần tử span sẽ giúp hỗ trợ khả năng truy cập bằng Ứng dụng Internet đa dạng có thể truy cập (ARIA) .

Lưu và đóng file . Mở App.js để hiển thị thành phần:

  • nano src/components/App/App.js

Nhập SaladMaker và hiển thị sau thành phần Navigation :

state-context-tutorial / src / components / App / App.js
import React from 'react'; import Navigation from '../Navigation/Navigation'; import SaladMaker from '../SaladMaker/SaladMaker';  function App() {   return (     <>       <Navigation />       <SaladMaker />     </>   ); }  export default App; 

Lưu và đóng file . Khi bạn làm như vậy, trang sẽ reload và bạn sẽ thấy tiêu đề:

Trang làm salad

Tiếp theo, tạo một thành phần có tên là SaladItem . Đây sẽ là một thẻ cho từng thành phần riêng lẻ.

Mở file trong editor của bạn:

  • nano src/components/SaladItem/SaladItem.js

Thành phần này sẽ có ba phần: tên của món, một biểu tượng hiển thị món đó có phải là món yêu thích của user hay không và một biểu tượng cảm xúc được đặt bên trong một nút sẽ thêm món đó vào món salad khi nhấp vào. Thêm các dòng sau vào SaladItem.js :

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React from 'react'; import PropTypes from 'prop-types'; import { createUseStyles } from 'react-jss';  const useStyles = createUseStyles({   add: {     background: 'none',     border: 'none',     cursor: 'pointer',   },   favorite: {     fontSize: 20,     position: 'absolute',     top: 10,     right: 10,   },   image: {     fontSize: 80   },   wrapper: {     border: 'lightgrey solid 1px',     margin: 20,     padding: 25,     position: 'relative',     textAlign: 'center',     textTransform: 'capitalize',     width: 200,   } });  export default function SaladItem({ image, name }) {   const classes = useStyles();   const favorite = true;   return(     <div className={classes.wrapper}>         <h3>           {name}         </h3>         <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>           {favorite ? '😋' : ''}         </span>         <button className={classes.add}>           <span className={classes.image} role="img" aria-label={name}>{image}</span>         </button>     </div>   ) }  SaladItem.propTypes = {   image: PropTypes.string.isRequired,   name: PropTypes.string.isRequired, } 

imagename sẽ là đạo cụ. Mã sử dụng biến favoritetoán tử bậc ba để xác định có điều kiện xem biểu tượng favorite xuất hiện hay không. Biến favorite sau đó sẽ được xác định với ngữ cảnh như một phần của profile user . Bây giờ, hãy đặt nó thành true . Tạo kiểu sẽ đặt biểu tượng yêu thích ở góc trên bên phải của thẻ và xóa đường viền và nền mặc định trên nút. Lớp wrapper sẽ thêm một đường viền nhỏ và biến đổi một số văn bản. Cuối cùng, PropTypes bổ sung một hệ thống đánh máy yếu để cung cấp một số thực thi nhằm đảm bảo loại prop không được thông qua.

Lưu và đóng file . Bây giờ, bạn cần kết xuất các mục khác nhau. Bạn sẽ làm điều đó với một thành phần được gọi là SaladBuilder , sẽ chứa danh sách các mục mà nó sẽ chuyển đổi thành một loạt các thành phần SaladItem :

Mở SaladBuilder :

  • nano src/components/SaladBuilder/SaladBuilder.js

Nếu đây là ứng dụng production , dữ liệu này thường đến từ Giao diện lập trình ứng dụng (API). Nhưng hiện tại, hãy sử dụng danh sách thành phần được mã hóa cứng:

state-context-tutorial / src / components / SaladBuilder / SaladBuilder.js
import React from 'react'; import SaladItem from '../SaladItem/SaladItem';  import { createUseStyles } from 'react-jss';  const useStyles = createUseStyles({   wrapper: {     display: 'flex',     flexWrap: 'wrap',     padding: [10, 50],     justifyContent: 'center',   } });  const ingredients = [   {     image: '🍎',     name: 'apple',   },   {     image: '🥑',     name: 'avocado',   },   {     image: '🥦',     name: 'broccoli',   },   {     image: '🥕',     name: 'carrot',   },   {     image: '🍷',     name: 'red wine dressing',   },   {     image: '🍚',     name: 'seasoned rice',   }, ];  export default function SaladBuilder() {   const classes = useStyles();   return(     <div className={classes.wrapper}>       {         ingredients.map(ingredient => (           <SaladItem             key={ingredient.name}             image={ingredient.image}             name={ingredient.name}           />         ))       }     </div>   ) } 

Đoạn mã này sử dụng phương thức mảng map() để ánh xạ qua từng mục trong danh sách, chuyển nameimage làm đạo cụ cho một thành phần SaladItem . Đảm bảo thêm key cho mỗi mục khi bạn lập bản đồ . Kiểu cho thành phần này thêm một màn hình hiển thị flex cho bố cục flexbox , bao bọc các thành phần và căn giữa chúng.

Lưu và đóng file .

Cuối cùng, kết xuất thành phần trong SaladMaker để nó sẽ xuất hiện trong trang.

Mở SaladMaker :

  • nano src/components/SaladMaker/SaladMaker.js

Sau đó nhập SaladBuilder và hiển thị sau tiêu đề:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react'; import { createUseStyles } from 'react-jss'; import SaladBuilder from '../SaladBuilder/SaladBuilder';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export default function SaladMaker() {   const classes = useStyles();   return(     <>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>       <SaladBuilder />     </>   ) } 

Lưu và đóng file . Khi bạn thực hiện, trang sẽ reload và bạn sẽ tìm thấy nội dung:

Thợ làm salad với các mặt hàng

Bước cuối cùng là thêm phần tóm tắt của món salad đang làm. Thành phần này sẽ hiển thị danh sách các mục mà user đã chọn. Hiện tại, bạn sẽ mã hóa các mục. Bạn sẽ cập nhật chúng với ngữ cảnh trong Bước 3.

Mở SaladSummary trong editor của bạn:

  • nano src/components/SaladSummary/SaladSummary.js

Thành phần sẽ là một tiêu đề và một danh sách các mục chưa được sắp xếp. Bạn sẽ sử dụng flexbox để bọc chúng:

state-context-tutorial / src / components / SaladSummary / SaladSummary.jss
import React from 'react'; import { createUseStyles } from 'react-jss';  const useStyles = createUseStyles({   list: {     display: 'flex',     flexDirection: 'column',     flexWrap: 'wrap',     maxHeight: 50,     '& li': {       width: 100     }   },   wrapper: {     borderTop: 'black solid 1px',     display: 'flex',     padding: 25,   } });  export default function SaladSummary() {   const classes = useStyles();   return(     <div className={classes.wrapper}>       <h2>Your Salad</h2>       <ul className={classes.list}>         <li>Apple</li>         <li>Avocado</li>         <li>Carrots</li>       </ul>     </div>   ) } 

Lưu các file . Sau đó, mở SaladMaker để hiển thị mục:

  • nano src/components/SaladMaker/SaladMaker.js

Nhập và thêm SaladSummary sau SaladBuilder :

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react'; import { createUseStyles } from 'react-jss'; import SaladBuilder from '../SaladBuilder/SaladBuilder'; import SaladSummary from '../SaladSummary/SaladSummary';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export default function SaladMaker() {   const classes = useStyles();   return(     <>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>       <SaladBuilder />       <SaladSummary />     </>   ) } 

Lưu và đóng file . Khi bạn làm như vậy, trang sẽ làm mới và bạn sẽ tìm thấy ứng dụng đầy đủ:

Trang web xây dựng salad

Có dữ liệu được chia sẻ trong toàn bộ ứng dụng. Thành phần Navigation và thành phần SaladItem đều cần biết điều gì đó về user : tên và danh sách yêu thích của họ. SaladItem cũng cần cập nhật dữ liệu có thể truy cập được trong thành phần SaladSummary . Các thành phần có chung tổ tiên, nhưng việc truyền dữ liệu xuống cây sẽ khó và dễ bị lỗi.

Đó là nơi xuất hiện ngữ cảnh. Bạn có thể khai báo dữ liệu trong một nguồn root chung và sau đó truy cập sau đó mà không cần chuyển nó xuống hệ thống phân cấp của các thành phần một cách rõ ràng.

Trong bước này, bạn đã tạo một ứng dụng để cho phép user tạo món salad từ danh sách các tùy chọn. Bạn đã tạo một tập hợp các thành phần cần truy cập hoặc cập nhật dữ liệu được kiểm soát bởi các thành phần khác. Trong bước tiếp theo, bạn sẽ sử dụng ngữ cảnh để lưu trữ dữ liệu và truy cập nó trong các thành phần con.

Bước 2 - Cung cấp dữ liệu từ thành phần root

Trong bước này, bạn sẽ sử dụng ngữ cảnh để lưu trữ thông tin khách hàng ở root của thành phần. Bạn sẽ tạo một ngữ cảnh tùy chỉnh, sau đó sử dụng một thành phần gói đặc biệt được gọi là Provider sẽ lưu trữ thông tin ở root của dự án. Sau đó, bạn sẽ sử dụng useContext Hook để kết nối với trình cung cấp trong các thành phần lồng nhau để bạn có thể hiển thị thông tin tĩnh. Khi kết thúc bước này, bạn có thể cung cấp kho thông tin tập trung và sử dụng thông tin được lưu trữ trong ngữ cảnh ở nhiều thành phần khác nhau.

Bối cảnh cơ bản nhất của nó là một giao diện để chia sẻ thông tin. Nhiều ứng dụng có một số thông tin chung mà chúng cần chia sẻ trên toàn ứng dụng, chẳng hạn như tùy chọn của user , thông tin chủ đề và các thay đổi ứng dụng trên toàn bộ trang web. Với ngữ cảnh, bạn có thể lưu trữ thông tin đó ở cấp cơ sở sau đó truy cập nó ở bất kỳ đâu. Vì bạn đặt thông tin ở chế độ cha mẹ, bạn biết thông tin đó sẽ luôn có sẵn và sẽ luôn được cập nhật.

Để thêm ngữ cảnh, hãy tạo một folder mới có tên là User :

  • mkdir src/components/User

User sẽ không phải là một thành phần truyền thống, ở chỗ bạn sẽ sử dụng nó như một thành phần và như một phần dữ liệu cho một Hook đặc biệt được gọi là useContext . Hiện tại, hãy giữ cấu trúc file phẳng, nhưng nếu bạn sử dụng nhiều ngữ cảnh, có thể đáng để chuyển chúng sang một cấu trúc folder khác.

Tiếp theo, mở User.js trong editor của bạn:

  • nano src/components/User/User.js

Bên trong file , nhập hàm createContext từ React, sau đó thực thi hàm và xuất kết quả:

state-context-tutorial / src / components / User / User.js
import { createContext } from 'react';  const UserContext = createContext(); export default UserContext; 

Bằng cách thực thi chức năng, bạn đã đăng ký ngữ cảnh. Kết quả, UserContext , là những gì bạn sẽ sử dụng trong các thành phần của bạn .

Lưu và đóng file .

Bước tiếp theo là áp dụng ngữ cảnh cho một tập hợp các phần tử. Để làm điều đó, bạn sẽ sử dụng một thành phần được gọi là Provider . Provider là một thành phần cài đặt dữ liệu và sau đó bao bọc một số thành phần con. Bất kỳ thành phần con nào được bao bọc sẽ có quyền truy cập vào dữ liệu từ Provider với useContext Hook.

Vì dữ liệu user sẽ không đổi trong toàn bộ dự án, hãy đặt nó lên cây thành phần càng cao càng tốt. Trong ứng dụng này, bạn sẽ đặt nó ở cấp cơ sở trong thành phần App :

Mở App :

  • nano src/components/App/App.js

Thêm các dòng mã được đánh dấu sau để nhập ngữ cảnh và chuyển dữ liệu:

state-context-tutorial / src / components / App / App.js
import React from 'react'; import Navigation from '../Navigation/Navigation'; import SaladMaker from '../SaladMaker/SaladMaker'; import UserContext from '../User/User';  const user = {   name: 'Kwame',   favorites: [     'avocado',     'carrot'   ] }  function App() {   return (     <UserContext.Provider value={user}>       <Navigation />       <SaladMaker />     </UserContext.Provider>   ); }  export default App; 

Trong một ứng dụng thông thường, bạn sẽ tìm nạp dữ liệu user hoặc lưu trữ dữ liệu đó trong quá trình hiển thị phía server . Trong trường hợp này, bạn đã mã hóa cứng một số dữ liệu mà bạn có thể nhận được từ một API. Bạn đã tạo một đối tượng có tên là user chứa tên user dưới dạng một chuỗi và một mảng các thành phần yêu thích.

Tiếp theo, bạn đã nhập UserContext , sau đó bao bọc NavigationSaladMaker bằng một thành phần được gọi là UserContext.Provider . Chú ý trong trường hợp này UserContext hoạt động như thế nào như một thành phần React tiêu chuẩn. Thành phần này sẽ nhận một value hỗ trợ duy nhất được gọi là. Chỗ dựa đó sẽ là dữ liệu bạn muốn chia sẻ, trong trường hợp này là đối tượng user .

Lưu và đóng file . Bây giờ dữ liệu có sẵn trong toàn bộ ứng dụng. Tuy nhiên, để sử dụng dữ liệu, bạn cần nhập và truy cập ngữ cảnh .

Đến đây bạn đã cài đặt ngữ cảnh, bạn có thể bắt đầu thay thế dữ liệu được mã hóa cứng trong thành phần của bạn bằng các giá trị động. Bắt đầu bằng cách thay thế tên được mã hóa cứng trong Navigation bằng dữ liệu user mà bạn đã đặt với UserContext.Provider .

Mở Navigation.js :

  • nano src/components/Navigation/Navigation.js

Bên trong Navigation , nhập useContext từ React và UserContext từ folder thành phần. Sau đó, gọi useContext bằng cách sử dụng UserContext làm đối số. Không giống như UserContext.Provider , bạn không cần phải hiển thị UserContext trong JSX . Hook sẽ trả về dữ liệu mà bạn đã cung cấp trong value chống đỡ. Lưu dữ liệu vào một biến mới được gọi là user , là một đối tượng chứa namefavorites . Sau đó, bạn có thể thay thế tên được mã hóa cứng bằng user.name :

state-context-tutorial / src / components / Navigation / Navigation.js
import React, { useContext } from 'react'; import { createUseStyles } from 'react-jss';  import UserContext from '../User/User';  const useStyles = createUseStyles({   wrapper: {     outline: 'black solid 1px',     padding: [15, 10],     textAlign: 'right',   } });  export default function Navigation() {   const user = useContext(UserContext);   const classes = useStyles();   return(     <div className={classes.wrapper}>       Welcome, {user.name}     </div>   ) } 

UserContext hoạt động như một thành phần trong App.js , nhưng ở đây bạn đang sử dụng nó nhiều hơn như một phần dữ liệu. Tuy nhiên, nó vẫn có thể hoạt động như một thành phần nếu bạn muốn. Bạn có thể truy cập vào cùng một dữ liệu bằng cách sử dụng Consumer là một phần của UserContext . Bạn truy xuất dữ liệu bằng cách thêm UserContext.Consumer vào JSX của bạn , sau đó sử dụng một hàm khi còn nhỏ để truy cập dữ liệu .

Mặc dù có thể sử dụng thành phần Consumer , nhưng sử dụng Móc thường có thể ngắn hơn và dễ đọc hơn, trong khi vẫn cung cấp cùng một thông tin cập nhật. Đây là lý do tại sao hướng dẫn này sử dụng cách tiếp cận Hooks.

Lưu và đóng file . Khi bạn làm như vậy, trang sẽ làm mới và bạn sẽ thấy cùng một tên. Nhưng lần này nó đã cập nhật động:

Trang web xây dựng salad

Trong trường hợp này, dữ liệu không di chuyển qua nhiều thành phần. Cây thành phần đại diện cho đường dẫn mà dữ liệu được truyền đi sẽ trông giống như sau:

| UserContext.Provider   | Navigation 

Bạn có thể chuyển tên user này làm chỗ dựa và ở quy mô này, đó có thể là một chiến lược hiệu quả. Nhưng khi ứng dụng phát triển, có khả năng thành phần Navigation sẽ di chuyển. Có thể có một thành phần được gọi là Header bao bọc thành phần Navigation và một thành phần khác như TitleBar , hoặc có thể bạn sẽ tạo một thành phần Template và sau đó lồng Navigation vào đó. Bằng cách sử dụng ngữ cảnh, bạn sẽ không phải cấu trúc lại Navigation miễn là Provider có sẵn, giúp việc tái cấu trúc dễ dàng hơn.

Thành phần tiếp theo cần dữ liệu user là thành phần SaladItem . Trong thành phần SaladItem , bạn cần mảng yêu thích của user . Bạn sẽ hiển thị biểu tượng cảm xúc một cách có điều kiện nếu thành phần được user yêu thích.

Mở SaladItem.js :

  • nano src/components/SaladItem/SaladItem.js

Nhập useContextUserContext , sau đó gọi useContext bằng UserContext . Sau đó, kiểm tra xem thành phần có trong favorites mảng bằng cách sử dụng includes phương pháp:

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { createUseStyles } from 'react-jss';  import UserContext from '../User/User';  const useStyles = createUseStyles({ ... });  export default function SaladItem({ image, name }) {   const classes = useStyles();   const user = useContext(UserContext);   const favorite = user.favorites.includes(name);   return(     <div className={classes.wrapper}>         <h3>           {name}         </h3>         <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>           {favorite ? '😋' : ''}         </span>         <button className={classes.add}>           <span className={classes.image} role="img" aria-label={name}>{image}</span>         </button>     </div>   ) }  SaladItem.propTypes = {   image: PropTypes.string.isRequired,   name: PropTypes.string.isRequired, } 

Lưu và đóng file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và bạn sẽ thấy rằng chỉ những mục yêu thích mới có biểu tượng cảm xúc:

Món làm salad với bơ và cà rốt được yêu thích

Không giống như Navigation , bối cảnh đang di chuyển xa hơn nhiều. Cây thành phần sẽ trông giống như sau:

| User.Provider   | SaladMaker     | SaladBuilder       | SaladItem 

Thông tin bỏ qua hai thành phần trung gian mà không có bất kỳ đạo cụ nào. Nếu bạn phải chuyển dữ liệu dưới dạng phần mềm hỗ trợ qua toàn bộ cây, sẽ rất nhiều công việc và bạn có nguy cơ gặp phải một nhà phát triển trong tương lai cấu trúc lại mã và quên chuyển phần hỗ trợ xuống. Với ngữ cảnh, bạn có thể tin tưởng mã sẽ hoạt động khi ứng dụng phát triển và phát triển.

Trong bước này, bạn đã tạo bối cảnh và sử dụng Trình Provider để đặt dữ liệu trong cây thành phần. Bạn cũng đã truy cập ngữ cảnh với useContext Hook và sử dụng ngữ cảnh trên nhiều thành phần. Dữ liệu này là tĩnh và do đó không bao giờ thay đổi sau khi cài đặt ban đầu, nhưng sẽ có lúc bạn cần chia sẻ dữ liệu và cũng có thể sửa đổi dữ liệu trên nhiều thành phần. Trong bước tiếp theo, bạn sẽ cập nhật dữ liệu lồng nhau bằng ngữ cảnh.

Bước 3 - Cập nhật dữ liệu từ các thành phần lồng nhau

Trong bước này, bạn sẽ sử dụng ngữ cảnh và useReducer Hook để tạo dữ liệu động mà các thành phần lồng nhau có thể sử dụng và cập nhật. Bạn sẽ cập nhật các thành phần SaladItem của bạn để cài đặt dữ liệu mà SaladSummary sẽ sử dụng và hiển thị. Bạn cũng sẽ đặt các trình cung cấp ngữ cảnh bên ngoài thành phần root . Đến cuối bước này, bạn sẽ có một ứng dụng có thể sử dụng và cập nhật dữ liệu trên một số thành phần và bạn có thể thêm nhiều trình cung cấp ngữ cảnh ở các cấp khác nhau của ứng dụng.

Đến đây, ứng dụng của bạn đang hiển thị dữ liệu user trên nhiều thành phần, nhưng nó thiếu bất kỳ sự tương tác nào của user . Trong bước trước, bạn đã sử dụng ngữ cảnh để chia sẻ một phần dữ liệu, nhưng bạn cũng có thể chia sẻ một tập hợp dữ liệu, bao gồm cả các hàm. Điều đó nghĩa là bạn có thể chia sẻ dữ liệu và cũng có thể chia sẻ chức năng cập nhật dữ liệu.

Trong ứng dụng của bạn, mỗi SaladItem cần cập nhật danh sách được chia sẻ. Sau đó, thành phần SaladSummary của bạn sẽ hiển thị các mục mà user đã chọn và thêm nó vào danh sách. Vấn đề là các thành phần này không phải là con cháu trực tiếp, vì vậy bạn không thể chuyển dữ liệu và các chức năng cập nhật làm đạo cụ. Nhưng họ có chung một cha mẹ: SaladMaker .

Một trong những điểm khác biệt lớn giữa ngữ cảnh và các giải pháp quản lý nhà nước khác như Redux là ngữ cảnh không nhằm mục đích trở thành một cửa hàng trung tâm. Bạn có thể sử dụng nó nhiều lần trong một ứng dụng và khởi tạo nó ở cấp root hoặc sâu trong cây thành phần. Nói cách khác, bạn có thể dàn trải các ngữ cảnh của bạn trong toàn bộ ứng dụng, tạo ra các bộ sưu tập dữ liệu tập trung mà không lo xung đột.

Để giữ cho ngữ cảnh được tập trung, hãy tạo Providers bao bọc cha mẹ được chia sẻ gần nhất khi có thể. Trong trường hợp này, điều đó nghĩa là , thay vì thêm một ngữ cảnh khác trong App , bạn sẽ thêm ngữ cảnh trong thành phần SaladMaker .

Mở SaladMaker :

  • nano src/components/SaladMaker/SaladMaker.js

Sau đó, tạo và xuất một ngữ cảnh mới có tên là SaladContext :

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { createContext } from 'react'; import { createUseStyles } from 'react-jss'; import SaladBuilder from '../SaladBuilder/SaladBuilder'; import SaladSummary from '../SaladSummary/SaladSummary';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export const SaladContext = createContext();  export default function SaladMaker() {   const classes = useStyles();   return(     <>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>       <SaladBuilder />       <SaladSummary />     </>   ) } 

Trong bước trước, bạn đã tạo một thành phần riêng cho ngữ cảnh của bạn , nhưng trong trường hợp này, bạn đang tạo nó trong cùng một file mà bạn đang sử dụng nó. Vì User dường như không liên quan trực tiếp đến App , nên việc tách chúng ra có thể hợp lý hơn. Tuy nhiên, vì SaladContext được gắn chặt với thành phần SaladMaker nên việc giữ chúng lại với nhau sẽ tạo ra nhiều mã dễ đọc hơn.

Ngoài ra, bạn có thể tạo một ngữ cảnh chung hơn được gọi là OrderContext , bạn có thể sử dụng lại trên nhiều thành phần. Trong trường hợp đó, bạn muốn tạo một thành phần riêng biệt. Còn bây giờ, hãy giữ chúng bên nhau. Bạn luôn có thể cấu trúc lại sau nếu bạn quyết định chuyển sang một mẫu khác.

Trước khi bạn thêm Provider hãy suy nghĩ về dữ liệu bạn muốn chia sẻ. Bạn cần một mảng các mục và một hàm để thêm các mục. Không giống như các công cụ quản lý trạng thái tập trung khác, ngữ cảnh không xử lý các cập nhật cho dữ liệu . Nó chỉ giữ dữ liệu để sử dụng sau này. Để cập nhật dữ liệu, bạn cần sử dụng các công cụ quản lý trạng thái khác như Hooks. Nếu bạn đang thu thập dữ liệu cho cùng một thành phần, bạn sẽ sử dụng useState hoặc useReducer Hooks. Nếu bạn chưa quen với các Hook này, hãy xem Cách quản lý trạng thái với Hooks trên các thành phần React .

useReducer Hook rất phù hợp vì bạn cần cập nhật trạng thái mới nhất cho mọi hành động.

Tạo một hàm reducer để thêm một mục mới vào một mảng state , sau đó sử dụng useReducer Hook để tạo một mảng salad và một hàm setSalad :

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { useReducer, createContext } from 'react'; import { createUseStyles } from 'react-jss'; import SaladBuilder from '../SaladBuilder/SaladBuilder'; import SaladSummary from '../SaladSummary/SaladSummary';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export const SaladContext = createContext();  function reducer(state, item) {   return [...state, item] }  export default function SaladMaker() {   const classes = useStyles();   const [salad, setSalad] = useReducer(reducer, []);   return(     <>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>       <SaladBuilder />       <SaladSummary />     </>   ) } 

Đến đây bạn có một thành phần chứa dữ liệu salad mà bạn muốn chia sẻ, một chức năng được gọi là setSalad để cập nhật dữ liệu và SaladContext để chia sẻ dữ liệu trong cùng một thành phần. Lúc này, bạn cần kết hợp chúng lại với nhau.

Để kết hợp, bạn cần tạo Provider . Vấn đề là Provider lấy một value duy nhất làm chỗ dựa. Vì bạn không thể chuyển saladsetSalad riêng lẻ, bạn cần kết hợp chúng thành một đối tượng và chuyển đối tượng dưới dạng value :

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { useReducer, createContext } from 'react'; import { createUseStyles } from 'react-jss'; import SaladBuilder from '../SaladBuilder/SaladBuilder'; import SaladSummary from '../SaladSummary/SaladSummary';  const useStyles = createUseStyles({   wrapper: {     textAlign: 'center',   } });  export const SaladContext = createContext();  function reducer(state, item) {   return [...state, item] }  export default function SaladMaker() {   const classes = useStyles();   const [salad, setSalad] = useReducer(reducer, []);   return(     <SaladContext.Provider value={{ salad, setSalad }}>       <h1 className={classes.wrapper}>         <span role="img" aria-label="salad">🥗 </span>           Build Your Custom Salad!           <span role="img" aria-label="salad"> 🥗</span>       </h1>       <SaladBuilder />       <SaladSummary />     </SaladContext.Provider>   ) } 

Lưu và đóng file . Như với Navigation , có vẻ như không cần thiết phải tạo ngữ cảnh khi SaladSummary nằm trong cùng một thành phần với ngữ cảnh. Chuyển salad làm chỗ dựa là hoàn toàn hợp lý, nhưng bạn có thể phải tái cấu trúc nó sau đó. Sử dụng ngữ cảnh ở đây giữ thông tin cùng nhau ở một nơi duy nhất.

Tiếp theo, đi vào thành phần SaladItem và kéo hàm setSalad ra khỏi ngữ cảnh.

Mở thành phần trong editor :

  • nano src/components/SaladItem/SaladItem.js

Bên trong SaladItem , nhập bối cảnh từ SaladMaker , sau đó kéo ra hàm setSalad bằng cách sử dụng hàm hủy. Thêm sự kiện nhấp chuột vào nút sẽ gọi hàm setSalad . Vì bạn muốn user có thể thêm một mục nhiều lần, bạn cũng cần tạo một id duy nhất cho từng mục để chức năng map có thể chỉ định một key duy nhất:

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React, { useReducer, useContext } from 'react'; import PropTypes from 'prop-types'; import { createUseStyles } from 'react-jss';  import UserContext from '../User/User'; import { SaladContext } from '../SaladMaker/SaladMaker';  const useStyles = createUseStyles({ ... });  const reducer = key => key + 1; export default function SaladItem({ image, name }) {   const classes = useStyles();   const { setSalad } = useContext(SaladContext)   const user = useContext(UserContext);   const favorite = user.favorites.includes(name);   const [id, updateId] = useReducer(reducer, 0);   function update() {     setSalad({       name,       id: `${name}-${id}`     })     updateId();   };   return(     <div className={classes.wrapper}>         <h3>           {name}         </h3>         <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>           {favorite ? '😋' : ''}         </span>         <button className={classes.add} onClick={update}>           <span className={classes.image} role="img" aria-label={name}>{image}</span>         </button>     </div>   ) } ... 

Để tạo id duy nhất, bạn sẽ sử dụng useReducer Hook để tăng giá trị trên mỗi lần nhấp. Đối với lần nhấp đầu tiên, id sẽ là 0 ; thứ hai sẽ là 1 , v.v. Bạn sẽ không bao giờ hiển thị giá trị này cho user ; điều này sẽ chỉ tạo ra một giá trị duy nhất cho hàm ánh xạ sau này.

Sau khi tạo id duy nhất, bạn đã tạo một hàm được gọi là update để tăng id và gọi setSalad . Cuối cùng, bạn đã gắn chức năng vào nút với onClick prop.

Lưu và đóng file . Bước cuối cùng là kéo dữ liệu động từ ngữ cảnh trong SaladSummary .

Mở SaladSummary

  • nano src/components/SaladSummary/SaladSummary.js

Nhập thành phần SaladContext , sau đó lấy dữ liệu salad bằng cách sử dụng hàm hủy . Thay thế các mục trong danh sách được mã hóa cứng bằng một chức năng ánh xạ trên salad , chuyển đổi các đối tượng thành phần tử <li> . Đảm bảo sử dụng id làm key :

state-context-tutorial / src / components / SaladSummary / SaladSummary.js
import React, { useContext } from 'react'; import { createUseStyles } from 'react-jss';  import { SaladContext } from '../SaladMaker/SaladMaker';  const useStyles = createUseStyles({ ... });  export default function SaladSummary() {   const classes = useStyles();   const { salad } = useContext(SaladContext);   return(     <div className={classes.wrapper}>       <h2>Your Salad</h2>       <ul className={classes.list}>         {salad.map(({ name, id }) => (<li key={id}>{name}</li>))}       </ul>     </div>   ) } 

Lưu và đóng file . Khi bạn làm như vậy, bạn có thể nhấp vào các mục và nó sẽ cập nhật tóm tắt:

Thêm các món salad

Lưu ý cách ngữ cảnh cung cấp cho bạn khả năng chia sẻ và cập nhật dữ liệu trong các thành phần khác nhau. Ngữ cảnh không tự cập nhật các mục, nhưng nó cung cấp cho bạn một cách để sử dụng useReducer Hook trên nhiều thành phần. Ngoài ra, bạn cũng có thể tự do đặt bối cảnh dưới cái cây. Có vẻ như tốt nhất là luôn giữ bối cảnh ở root , nhưng bằng cách giữ bối cảnh thấp hơn, bạn không phải lo lắng về trạng thái không sử dụng vẫn tồn tại trong một cửa hàng trung tâm. Ngay sau khi bạn ngắt kết nối một thành phần, dữ liệu sẽ không xuất hiện . Đó có thể là một vấn đề nếu bạn muốn lưu dữ liệu, nhưng trong trường hợp đó, bạn chỉ cần nâng ngữ cảnh lên mức root cao hơn.

Một lợi thế khác của việc sử dụng ngữ cảnh thấp hơn trong cây ứng dụng của bạn là bạn có thể sử dụng lại ngữ cảnh mà không lo xung đột. Giả sử bạn có một ứng dụng lớn hơn có máy làm bánh mì sandwich và máy làm salad. Bạn có thể tạo một ngữ cảnh chung được gọi là OrderContext và sau đó bạn có thể sử dụng nó tại nhiều điểm trong thành phần của bạn mà không cần lo lắng về xung đột dữ liệu hoặc tên. Nếu bạn có một SaladMaker và một SandwichMaker , cái cây sẽ trông giống như sau:

| App   | Salads     | OrderContext       | SaladMaker   | Sandwiches     | OrderContext       | SandwichMaker 

Lưu ý OrderContext ở đó hai lần. Điều đó tốt, vì useContext Hook sẽ tìm kiếm nhà cung cấp gần nhất.

Trong bước này, bạn đã chia sẻ và cập nhật dữ liệu bằng ngữ cảnh. Bạn cũng đã đặt ngữ cảnh bên ngoài phần tử root để nó gần với các thành phần cần thông tin mà không làm lộn xộn thành phần root . Cuối cùng, bạn đã kết hợp ngữ cảnh với Hooks quản lý trạng thái để tạo dữ liệu động và có thể truy cập trên một số thành phần.

Kết luận

Ngữ cảnh là một công cụ mạnh mẽ và linh hoạt cung cấp cho bạn khả năng lưu trữ và sử dụng dữ liệu trên một ứng dụng. Nó cung cấp cho bạn khả năng xử lý dữ liệu phân tán bằng các công cụ tích hợp mà không yêu cầu cài đặt hoặc cấu hình bổ sung nào của bên thứ ba.

Việc tạo ngữ cảnh có thể sử dụng lại rất quan trọng trên nhiều thành phần chung, chẳng hạn như biểu mẫu cần truy cập dữ liệu trên các phần tử hoặc chế độ xem tab cần bối cảnh chung cho cả tab và màn hình. Bạn có thể lưu trữ nhiều loại thông tin trong các ngữ cảnh bao gồm chủ đề, dữ liệu biểu mẫu, thông báo cảnh báo, v.v. Context cho phép bạn tự do xây dựng các thành phần có thể truy cập dữ liệu mà không cần lo lắng về cách chuyển dữ liệu qua các thành phần trung gian hoặc cách lưu trữ dữ liệu trong một cửa hàng tập trung mà không làm cho cửa hàng quá lớn.

Nếu bạn muốn xem thêm các hướng dẫn về React, hãy xem trang Chủ đề React của ta hoặc quay lại trang Cách viết mã trong chuỗi React.js .


Tags:

Các tin liên quan