React Context與setState詳解使用方法
React中的插槽(slot)
React對于需要插槽的情況非常靈活,有兩種方案可以實現(xiàn):
組件的children子元素;
props屬性傳遞React元素
children實現(xiàn)插槽
每個組件都可以獲取到 props.children:它包含組件的開始標簽和結(jié)束標簽之間的內(nèi)容。
App.jsx
import React, { Component } from 'react' import NavBar from './nav-bar' import NavBarTwo from './nav-bar-two' export class App extends Component { render() { const btn = <button>按鈕2</button> return ( <div> {/* 1.使用children實現(xiàn)插槽 */} <NavBar> <button>按鈕</button> <h2>哈哈哈</h2> <i>斜體文本</i> </NavBar> </div> ) } } export default App
NavBar.jsx
import React, { Component } from 'react' // import PropTypes from "prop-types" import "./style.css" export class NavBar extends Component { render() { const { children } = this.props console.log(children) return ( <div className='nav-bar'> <div className="left">{children[0]}</div> <div className="center">{children[1]}</div> <div className="right">{children[2]}</div> </div> ) } } // NavBar.propTypes = { // children: PropTypes.array // } export default NavBar
props實現(xiàn)插槽
app.jsx
import React, { Component } from 'react' import NavBar from './nav-bar' import NavBarTwo from './nav-bar-two' export class App extends Component { render() { const btn = <button>按鈕2</button> return ( <div> {/* 2.使用props實現(xiàn)插槽 */} <NavBarTwo leftSlot={btn} centerSlot={<h2>呵呵呵</h2>} rightSlot={<i>斜體2</i>} /> </div> ) } } export default App
NavBarTwo.jsx
import React, { Component } from 'react' export class NavBarTwo extends Component { render() { const { leftSlot, centerSlot, rightSlot } = this.props return ( <div className='nav-bar'> <div className="left">{leftSlot}</div> <div className="center">{centerSlot}</div> <div className="right">{rightSlot}</div> </div> ) } } export default NavBarTwo
Context應用場景
非父子組件數(shù)據(jù)的共享:
在開發(fā)中,比較常見的數(shù)據(jù)傳遞方式是通過props屬性自上而下(由父到子)進行傳遞。
但是對于有一些場景:比如一些數(shù)據(jù)需要在多個組件中進行共享(地區(qū)偏好、UI主題、用戶登錄狀態(tài)、用戶信息等)。
如果我們在頂層的App中定義這些信息,之后一層層傳遞下去,那么對于一些中間層不需要數(shù)據(jù)的組件來說,是一種冗余的操作。
但是,如果層級更多的話,一層層傳遞是非常麻煩,并且代碼是非常冗余的:
React提供了一個API:Context;
Context 提供了一種在組件之間共享此類值的方式,而不必顯式地通過組件樹的逐層傳遞 props;
Context 設計目的是為了共享那些對于一個組件樹而言是“全局”的數(shù)據(jù),例如當前認證的用戶、主題或首選語言;
Context相關API
React.createContext
創(chuàng)建一個需要共享的Context對象:
如果一個組件訂閱了Context,那么這個組件會從離自身最近的那個匹配的 Provider 中讀取到當前的context值;
defaultValue是組件在頂層查找過程中沒有找到對應的Provider,那么就使用默認值
theme-context.js
import React from "react" // 1.創(chuàng)建一個Context const ThemeContext = React.createContext({ color: "blue", size: 10 }) export default ThemeContext
user-context.js
import React from "react" // 1.創(chuàng)建一個Context const UserContext = React.createContext() export default UserContext
Context.Provider
每個 Context 對象都會返回一個 Provider React 組件,它允許消費組件訂閱 context 的變化:
Provider 接收一個 value 屬性,傳遞給消費組件;
一個 Provider 可以和多個消費組件有對應關系;
多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數(shù)據(jù);
當 Provider 的 value 值發(fā)生變化時,它內(nèi)部的所有消費組件都會重新渲染;
import React, { Component } from 'react' import Home from './Home' import ThemeContext from "./context/theme-context" import UserContext from './context/user-context' import Profile from './Profile' export class App extends Component { constructor() { super() this.state = { info: { name: "kobe", age: 30 } } } render() { const { info } = this.state return ( <div> <h2>App</h2> {/* 1.給Home傳遞數(shù)據(jù) */} {/* <Home name="why" age={18}/> <Home name={info.name} age={info.age}/> <Home {...info}/> */} {/* 2.普通的Home */} {/* 第二步操作: 通過ThemeContext中Provider中value屬性為后代提供數(shù)據(jù) */} <UserContext.Provider value={{nickname: "kobe", age: 30}}> <ThemeContext.Provider value={{color: "red", size: "30"}}> <Home {...info}/> </ThemeContext.Provider> </UserContext.Provider> <Profile/> </div> ) } } export default App
Class.contextType
掛載在 class 上的 contextType 屬性會被重賦值為一個由 React.createContext() 創(chuàng)建的 Context 對象:
這能讓你使用 this.context 來消費最近 Context 上的那個值;
你可以在任何生命周期中訪問到它,包括 render 函數(shù)中;
import React, { Component } from 'react' import ThemeContext from './context/theme-context' import UserContext from './context/user-context' export class HomeInfo extends Component { render() { // 4.第四步操作: 獲取數(shù)據(jù), 并且使用數(shù)據(jù) console.log(this.context) return ( <div> <h2>HomeInfo: {this.context.color}</h2> <UserContext.Consumer> { value => { return <h2>Info User: {value.nickname}</h2> } } </UserContext.Consumer> </div> ) } } // 3.第三步操作: 設置組件的contextType為某一個Context HomeInfo.contextType = ThemeContext export default HomeInfo
Context.Consumer
這里,React 組件也可以訂閱到 context 變更。這能讓你在 函數(shù)式組件 中完成訂閱 context。
這里需要 函數(shù)作為子元素(function as child)這種做法;
這個函數(shù)接收當前的 context 值,返回一個 React 節(jié)點;
什么時候使用Context.Consumer呢?
1.當使用value的組件是一個函數(shù)式組件時;
2.當組件中需要使用多個Context時;
import ThemeContext from "./context/theme-context" function HomeBanner() { return <div> {/* 函數(shù)式組件中使用Context共享的數(shù)據(jù) */} <ThemeContext.Consumer> { value => { return <h2> Banner theme:{value.color}</h2> } } </ThemeContext.Consumer> </div> } export default HomeBanner
我們什么使用setState
開發(fā)中我們并不能直接通過修改state的值來讓界面發(fā)生更新:
因為我們修改了state之后,希望React根據(jù)最新的State來重新渲染界面,但是這種方式的修改React并不知道數(shù)據(jù)發(fā)生了變化;
React并沒有實現(xiàn)類似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式來監(jiān)聽數(shù)據(jù)的變化;
我們必須通過setState來告知React數(shù)據(jù)已經(jīng)發(fā)生了變化;
疑惑:在組件中并沒有實現(xiàn)setState的方法,為什么可以調(diào)用呢?
原因很簡單,setState方法是從Component中繼承過來的。
setState異步更新
setState的更新是異步的?
最終打印結(jié)果是Hello World;
可見setState是異步的操作,我們并不能在執(zhí)行完setState之后立馬拿到最新的state的結(jié)果
setState設計為異步,可以顯著的提升性能;
如果每次調(diào)用 setState都進行一次更新,那么意味著render函數(shù)會被頻繁調(diào)用,界面重新渲染,這樣效率是很低的;
最好的辦法應該是獲取到多個更新,之后進行批量更新;
如果同步更新了state,但是還沒有執(zhí)行render函數(shù),那么state和props不能保持同步;
state和props不能保持一致性,會在開發(fā)中產(chǎn)生很多的問題;
如何獲取異步的結(jié)果
式一:setState的回調(diào)
setState接受兩個參數(shù):第二個參數(shù)是一個回調(diào)函數(shù),這個回調(diào)函數(shù)會在更新后會執(zhí)行;
格式如下:setState(partialState, callback)
changeText() { this.setState({ message: '你好' }), () => { console.log(this.state.message) } }
當然我們也可以在生命周期函數(shù)
componentDidUpdate(precProps, provState, snapshot) { console.log(this.state.message) }
setState一定是異步的嗎(React18之前)
其實分成兩種情況:
在組件生命周期或React合成事件中,setState是異步;
在setTimeout或者原生dom事件中,setState是同步;
setState默認是異步的 (React18之后)
在React18之后,默認所有的操作都被放到了批處理中(異步處理)。
如果希望代碼可以同步會拿到,則需要執(zhí)行特殊的flushSync操作
import React, { Component } from 'react' import { flushSync } from 'react-dom' function Hello(props) { return <h2>{props.message}</h2> } export class App extends Component { constructor(props) { super(props) this.state = { message: "Hello World", counter: 0 } } componentDidMount() { // 1.網(wǎng)絡請求一: banners // 2.網(wǎng)絡請求二: recommends // 3.網(wǎng)絡請求三: productlist } changeText() { setTimeout(() => { // 在react18之前, setTimeout中setState操作, 是同步操作 // 在react18之后, setTimeout中setState異步操作(批處理) flushSync(() => { this.setState({ message: "你好啊, 李銀河" }) }) console.log(this.state.message) }, 0); } increment() { } render() { const { message, counter } = this.state console.log("render被執(zhí)行") return ( <div> <h2>message: {message}</h2> <button onClick={e => this.changeText()}>修改文本</button> <h2>當前計數(shù): {counter}</h2> <button onClick={e => this.increment()}>counter+1</button> <Hello message={message}/> </div> ) } } export default App
到此這篇關于React Context與setState詳解使用方法的文章就介紹到這了,更多相關React Context與setState內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
React+echarts?(echarts-for-react)?實現(xiàn)中國地圖及省份切換功能
這篇文章主要介紹了React+echarts?(echarts-for-react)?畫中國地圖及省份切換,有足夠的地圖數(shù)據(jù),可以點擊到街道,示例我只出到市級,本文結(jié)合實例代碼給大家介紹的非常詳細需要的朋友可以參考下2022-11-11詳解webpack + react + react-router 如何實現(xiàn)懶加載
這篇文章主要介紹了詳解webpack + react + react-router 如何實現(xiàn)懶加載,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-11-11React useMemo與useCallabck有什么區(qū)別
useCallback和useMemo是一樣的東西,只是入?yún)⒂兴煌瑄seCallback緩存的是回調(diào)函數(shù),如果依賴項沒有更新,就會使用緩存的回調(diào)函數(shù);useMemo緩存的是回調(diào)函數(shù)的return,如果依賴項沒有更新,就會使用緩存的return2022-12-12Zustand介紹與使用 React狀態(tài)管理工具的解決方案
本文主要介紹了Zustand,一種基于React的狀態(tài)管理庫,Zustand以簡潔易用、靈活性高及最小化原則等特點脫穎而出,旨在提供簡單而強大的狀態(tài)管理功能2024-10-10