ใน React บริบทคือวิธีการส่งข้อมูลระหว่างแผนผังส่วนประกอบโดยไม่ต้องเพิ่มอุปกรณ์ประกอบฉากสำหรับส่วนประกอบแต่ละชั้น บริบทให้วิธีการแบ่งปันค่าที่ระบุระหว่างส่วนประกอบต่างๆ โดยไม่ต้องส่งผ่านอุปกรณ์ประกอบฉากทีละชั้นอย่างชัดเจน ในต้นไม้
สภาพแวดล้อมการทำงานของบทช่วยสอนนี้: ระบบ Windows 10, รีแอคเวอร์ชัน 17.0.1, คอมพิวเตอร์ Dell G3
บริบทเป็นวิธีในการส่งข้อมูลระหว่างแผนผังส่วนประกอบโดยไม่ต้องเพิ่มอุปกรณ์ประกอบฉากไปยังส่วนประกอบแต่ละชั้นด้วยตนเอง ในแอปพลิเคชัน React ทั่วไป ข้อมูลจะถูกส่งจากบนลงล่าง (จากพาเรนต์ไปยังลูก) ผ่านอุปกรณ์ประกอบฉาก แต่วิธีการนี้ยุ่งยากมากสำหรับคุณสมบัติบางประเภท (เช่น การตั้งค่าภาษา ธีม UI) คุณสมบัติเหล่านี้จำเป็นสำหรับส่วนประกอบจำนวนมากใน ใบสมัคร บริบทเป็นวิธีการแบ่งปันค่าดังกล่าวระหว่างส่วนประกอบต่างๆ โดยไม่ต้องส่งอุปกรณ์ประกอบฉากผ่านแต่ละระดับของแผนผังส่วนประกอบอย่างชัดเจน
บริบทได้รับการออกแบบมาเพื่อแบ่งปันข้อมูลที่เป็น "ทั่วโลก" ไปยังแผนผังส่วนประกอบ เช่น ผู้ใช้ ธีม หรือภาษาที่ต้องการที่ได้รับการรับรองความถูกต้องในปัจจุบัน ตัวอย่างเช่น ในโค้ดต่อไปนี้ เราปรับสไตล์ของส่วนประกอบปุ่มด้วยตนเองผ่านแอตทริบิวต์ "theme"
class App ขยาย React.Component { render() { return <Toolbar theme="dark" />; }}function Toolbar(props) { // คอมโพเนนต์ Toolbar ยอมรับคุณสมบัติ "theme" เพิ่มเติม ซึ่งจะถูกส่งต่อไปยัง ThemedButton ส่วนประกอบ. // มันจะยุ่งยากหากทุกปุ่มในแอปพลิเคชันจำเป็นต้องทราบค่าของธีม // เพราะค่านี้จะต้องถูกส่งผ่านไปยังส่วนประกอบทั้งหมด return ( <p> <ThemedButton theme={props.theme} /> </p> );} คลาส ThemedButton ขยาย React.Component { render() { return <Button theme={this.props.theme} /> } }// ส่งผ่านอุปกรณ์ประกอบฉาก: App -> Toolbar -> ThemedButton// หากการซ้อนอุปกรณ์ประกอบฉากนั้นลึกมาก ดังนั้นอุปกรณ์ประกอบฉากจะต้องถูกส่งผ่านทีละชั้น แม้ว่าอุปกรณ์ประกอบฉากจะไม่จำเป็นต้องใช้ตรงกลางก็ตาม ก็ดูยุ่งยากมากเมื่อใช้บริบท เราสามารถหลีกเลี่ยงการส่งอุปกรณ์ประกอบฉากผ่านองค์ประกอบระดับกลางได้
// บริบทช่วยให้เราสามารถส่งค่าลึกเข้าไปในแผนผังส่วนประกอบโดยไม่ต้องส่งผ่านแต่ละส่วนประกอบอย่างชัดเจน // สร้างบริบทสำหรับธีมปัจจุบัน ("light" เป็นค่าเริ่มต้น) const ThemeContext = React.createContext('light');class App ขยาย React.Component { render() { // ใช้ Provider เพื่อส่งธีมปัจจุบันไปยังแผนผังส่วนประกอบต่อไปนี้ // ไม่ว่าจะลึกแค่ไหน องค์ประกอบใดๆ ก็สามารถอ่านค่านี้ได้ // ในตัวอย่างนี้ เราส่ง "dark" เป็นค่าปัจจุบัน return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); }}// คอมโพเนนต์ระดับกลางไม่จำเป็นต้องระบุธีมที่จะส่งต่ออีกต่อไป function Toolbar() { return ( <p> <ThemedButton /> </p> );}คลาส ThemedButton ขยาย React.Component { // ระบุ contextType เพื่ออ่านบริบทของธีมปัจจุบัน // React จะค้นหาผู้ให้บริการธีมที่ใกล้ที่สุดและใช้ค่าของมัน // ในตัวอย่างนี้ ค่าธีมปัจจุบันคือ "dark" static contextType = ThemeContext; render() { return <Button theme={this.context} />; }}// คุณยังสามารถใช้ ThemedButto.contextType = ThemeContext;สร้างวัตถุบริบท เมื่อ React เรนเดอร์ส่วนประกอบที่สมัครรับออบเจ็กต์บริบทนี้ ส่วนประกอบจะอ่านค่าบริบทปัจจุบันจากผู้ให้บริการที่ตรงกันซึ่งใกล้กับตัวเองมากที่สุดในแผนผังส่วนประกอบ
เฉพาะเมื่อไม่มีผู้ให้บริการที่ตรงกันในแผนผังที่มีส่วนประกอบอยู่ พารามิเตอร์ defaultValue จะมีผล ซึ่งจะช่วยในการทดสอบส่วนประกอบโดยไม่ต้องใช้ผู้ให้บริการเพื่อห่อส่วนประกอบ หมายเหตุ: เมื่อไม่ได้กำหนดถูกส่งผ่านไปยังค่าของผู้ให้บริการ ค่าเริ่มต้นของส่วนประกอบที่ใช้จะไม่มีผล
ออบเจ็กต์บริบทแต่ละรายการส่งคืนส่วนประกอบ Provider React ซึ่งช่วยให้ส่วนประกอบที่ใช้งานสามารถสมัครรับการเปลี่ยนแปลงบริบทได้
ผู้ให้บริการได้รับแอตทริบิวต์ค่าและส่งผ่านไปยังส่วนประกอบที่ใช้งาน ผู้ให้บริการสามารถมีความสัมพันธ์ที่สอดคล้องกับองค์ประกอบผู้บริโภคหลายรายการ สามารถใช้ผู้ให้บริการหลายรายซ้อนกันได้ และชั้นในจะเขียนทับข้อมูลของชั้นนอก
เมื่อค่าของผู้ให้บริการเปลี่ยนแปลงไป ส่วนประกอบที่ใช้งานภายในนั้นจะถูกเรนเดอร์ใหม่ ทั้งผู้ให้บริการและส่วนประกอบภายในของผู้บริโภคไม่อยู่ภายใต้ฟังก์ชัน shouldComponentUpdate ดังนั้นส่วนประกอบของผู้บริโภคจึงสามารถอัปเดตได้แม้ว่าองค์ประกอบระดับบนสุดจะออกจากการอัปเดตก็ตาม
แอตทริบิวต์ contextType ที่ติดตั้งบนคลาสจะถูกกำหนดใหม่ให้กับวัตถุบริบทที่สร้างโดย React.createContext() สิ่งนี้ช่วยให้คุณใช้ this.context เพื่อใช้ค่าในบริบทล่าสุด คุณสามารถเข้าถึงได้ในทุกวงจรชีวิต รวมถึงฟังก์ชันการเรนเดอร์ด้วย
import MyContext จาก './MyContext';class MyClass ขยาย React.Component { componentDidMount() { la value = this.context; /* หลังจากที่คอมโพเนนต์ถูกเมาท์แล้ว ให้ใช้ค่าของคอมโพเนนต์ MyContext เพื่อดำเนินการผลข้างเคียงบางอย่าง* / }componentDidUpdate() { la value = this.context; /* ... */ }componentWillUnmount() { la value = this.context; /* ... */ } render() { la value = this.context ; /* แสดงผลตามค่าขององค์ประกอบ MyContext*/ } // หรือใช้ static contextType = MyContext;}MyClass.contextType = MyContext;ที่นี่ ส่วนประกอบ React ยังสามารถสมัครรับการเปลี่ยนแปลงบริบทได้อีกด้วย ซึ่งช่วยให้คุณสามารถสมัครรับบริบทในส่วนประกอบการทำงานได้
สิ่งนี้ต้องการฟังก์ชั่นตั้งแต่ยังเป็นเด็ก ฟังก์ชันนี้รับค่าบริบทปัจจุบันและส่งกลับโหนด React ค่าที่ส่งผ่านไปยังฟังก์ชันจะเท่ากับค่าที่ผู้ให้บริการให้ไว้ใกล้กับบริบทนี้มากที่สุดในแผนผังส่วนประกอบ หากไม่มีผู้ให้บริการที่เกี่ยวข้อง พารามิเตอร์ value จะเทียบเท่ากับ defaultValue ที่ส่งไปยัง createContext()
วัตถุบริบทยอมรับคุณสมบัติชื่อ displayName ซึ่งเป็นประเภทสตริง React DevTools ใช้สตริงนี้เพื่อกำหนดบริบทที่จะแสดง
ส่วนประกอบต่อไปนี้จะปรากฏเป็น MyDisplayName ใน DevTools
const MyContext = React.createContext(/* ค่าบางค่า */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" ใน DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" ใน DevToolsสำหรับตัวอย่างธีมข้างต้น การใช้งานที่ซับซ้อนมากขึ้นสามารถทำได้โดยใช้ค่าไดนามิก
ธีม-context.js
ส่งออกธีม const = { light: { เบื้องหน้า: '#000000', พื้นหลัง: '#eeeeee', }, มืด: { เบื้องหน้า: '#ffffff', พื้นหลัง: '#222222', },}; ส่งออก const ThemeContext = React .createContext(themes.dark); // นี่คือค่าเริ่มต้นธีม-button.js
นำเข้า { ThemeContext } จาก './theme-context'; คลาส ThemedButton ขยาย React.Component { render() { la props = this.props; // รับค่าเริ่มต้นใน ThemeContext ให้ theme = this.context; return ( < button {...props} style={{พื้นหลังสี: theme.พื้นหลัง}} /> ); // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;ส่งออกค่าเริ่มต้น ThemedButton;แอพ.js
นำเข้า { ThemeContext, ธีม } จาก './theme-context'; นำเข้า ThemedButton จาก './themed-button';// ฟังก์ชันส่วนประกอบระดับกลางโดยใช้ ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > เปลี่ยนธีม </ThemedButton> );} แอปคลาสขยาย React.Component { ตัวสร้าง (อุปกรณ์ประกอบฉาก) { super(props); this.state = { theme: this.toggleTheme = () => { this .setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, })); }; } render() { // ใช้โดยส่วนประกอบปุ่ม ThemedButton ภายใน ThemeProvider ค่าในสถานะ // และส่วนประกอบภายนอกใช้ค่าธีมเริ่มต้นที่ส่งคืน ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> ); }}ReactDOM.render(<App />, document.root);// ส่วนประกอบที่ห่อโดย ThemeContext.Provider สามารถใช้ ThemeContext ค่า// ในแถบเครื่องมือ และ ThemedButton สามารถใช้ this.context เพื่อรับค่า// โปรดทราบว่าวิธีการอัปเดตสถานะคือการส่งต่อผ่านอุปกรณ์ประกอบฉาก และการอัพเดตจะถูกทริกเกอร์โดยส่วนประกอบที่สืบทอดมา วิธีการผ่านบริบทจะกล่าวถึงด้านล่างในตัวอย่างข้างต้น เราส่งฟังก์ชันอัปเดตผ่านอุปกรณ์ประกอบฉากเพื่อเปลี่ยนค่าของธีมในแอป เรารู้ว่าจำเป็นต้องอัปเดตบริบทจากส่วนประกอบที่ฝังลึกอยู่ในแผนผังส่วนประกอบ ในสถานการณ์สมมตินี้ คุณสามารถส่งผ่านฟังก์ชันผ่านบริบทเพื่อทำให้ส่วนประกอบของผู้บริโภคอัปเดตบริบทได้
ธีม-context.js
// ตรวจสอบให้แน่ใจว่าโครงสร้างข้อมูลค่าเริ่มต้นที่ส่งไปยัง createContext ตรงกับส่วนประกอบที่เรียก (ผู้บริโภค)! ส่งออก const ThemeContext = React.createContext ({ theme: theme.dark, toggleTheme: () => {}, // กำหนดวิธีการอัปเดตธีม, ส่งผ่านมัน});ธีมสลับปุ่ม js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // ปุ่ม Theme Toggler ไม่เพียงแต่รับค่าธีมเท่านั้น แต่ยังได้รับฟังก์ชันสลับธีมจากบริบท (ส่วน app.js ด้านล่าง) กลับ ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{พื้นหลังสี: theme.พื้นหลัง}}> สลับธีม </button> )} </ThemeContext.Consumer> ); } ส่งออก ThemeTogglerButton เริ่มต้น;แอพ.js
นำเข้า { ThemeContext, ธีม } จาก './theme-context'; นำเข้า ThemeTogglerButton จาก './theme-toggler-button'; แอพคลาสขยาย React.Component { ตัวสร้าง (อุปกรณ์ประกอบฉาก) { super (อุปกรณ์ประกอบฉาก); ) => { this.setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, })); }; // State ยังมีฟังก์ชันอัปเดตอยู่ด้วย จะถูกส่งผ่านไปยังผู้ให้บริการบริบท this.state = { theme: Themes.light, toggleTheme: this.toggleTheme, // กำหนดฟังก์ชันอัพเดตและส่งผ่านบริบท }; } render() { // สถานะทั้งหมดถูกส่งผ่านไปยังผู้ให้บริการ return ( < ThemeContext.Provider value={this.state}> <Content /> </ThemeContext.Provider> ); }}ฟังก์ชันเนื้อหา() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <แอป />, document.root);เพื่อให้แน่ใจว่าบริบทถูกเรนเดอร์ใหม่อย่างรวดเร็ว React จำเป็นต้องทำให้บริบทของส่วนประกอบผู้บริโภคแต่ละรายการเป็นโหนดแยกกันในแผนผังส่วนประกอบ
// บริบทของธีม ธีมเริ่มต้นคือค่า "light" const ThemeContext = React.createContext('light'); // บริบทการเข้าสู่ระบบของผู้ใช้ const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const { signedInUser, theme } = this.props; // ส่วนประกอบของแอปที่ให้การส่งคืนค่าบริบทเริ่มต้น ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < เค้าโครง /> </UserContext.Provider> </ThemeContext.Provider> }} เค้าโครงฟังก์ชัน () { กลับ ( <p> <Sidebar /> <เนื้อหา /> </p> );}// ส่วนประกอบ อาจใช้ฟังก์ชันบริบทหลายรายการ Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext . ผู้บริโภค> )} </ThemeContext.Consumer> );}หากมีการใช้ค่าบริบทตั้งแต่ 2 ค่าขึ้นไปร่วมกันบ่อยครั้ง คุณอาจต้องการพิจารณาสร้างองค์ประกอบการแสดงผลของคุณเองเพื่อให้ค่าเหล่านี้
เนื่องจากบริบทใช้ข้อมูลประจำตัวอ้างอิงเพื่อตัดสินใจว่าจะแสดงผลเมื่อใด อาจมีข้อผิดพลาดบางประการที่อาจกระตุ้นให้เกิดการแสดงผลโดยไม่คาดคิดในองค์ประกอบ Consumer เมื่อองค์ประกอบหลักของผู้ให้บริการแสดงผลอีกครั้ง ตัวอย่างเช่น ทุกครั้งที่ผู้ให้บริการถูกเรนเดอร์ใหม่ โค้ดต่อไปนี้จะเรนเดอร์คอมโพเนนต์คอนซูเมอร์ต่อไปนี้ทั้งหมดอีกครั้ง เนื่องจากแอตทริบิวต์ value ถูกกำหนดให้กับอ็อบเจ็กต์ใหม่เสมอ
แอปคลาสขยาย React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }}เพื่อป้องกันสถานการณ์นี้ สถานะของค่าจะถูกเลื่อนไปเป็นสถานะของโหนดพาเรนต์
แอพคลาสขยาย React.Component { ตัวสร้าง (อุปกรณ์ประกอบฉาก) { super (อุปกรณ์ประกอบฉาก); // หลังจากการเรนเดอร์หลายครั้ง สถานะจะยังคงอยู่ เมื่อค่าไม่เปลี่ยนแปลง ส่วนประกอบผู้บริโภคต่อไปนี้จะไม่เรนเดอร์ this.state = { ค่า: { บางสิ่งบางอย่าง: 'บางสิ่งบางอย่าง'}, }; } แสดงผล() { กลับ ( <ค่าของผู้ให้บริการ={this.state.value}> <แถบเครื่องมือ /> </ผู้ให้บริการ> }}