網站首頁 編程語言 正文
React中的插槽(slot)
React對于需要插槽的情況非常靈活,有兩種方案可以實現:
組件的children子元素;
props屬性傳遞React元素
children實現插槽
每個組件都可以獲取到 props.children:它包含組件的開始標簽和結束標簽之間的內容。
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實現插槽 */}
<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實現插槽
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實現插槽 */}
<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應用場景
非父子組件數據的共享:
在開發中,比較常見的數據傳遞方式是通過props屬性自上而下(由父到子)進行傳遞。
但是對于有一些場景:比如一些數據需要在多個組件中進行共享(地區偏好、UI主題、用戶登錄狀態、用戶信息等)。
如果我們在頂層的App中定義這些信息,之后一層層傳遞下去,那么對于一些中間層不需要數據的組件來說,是一種冗余的操作。
但是,如果層級更多的話,一層層傳遞是非常麻煩,并且代碼是非常冗余的:
React提供了一個API:Context;
Context 提供了一種在組件之間共享此類值的方式,而不必顯式地通過組件樹的逐層傳遞 props;
Context 設計目的是為了共享那些對于一個組件樹而言是“全局”的數據,例如當前認證的用戶、主題或首選語言;
Context相關API
React.createContext
創建一個需要共享的Context對象:
如果一個組件訂閱了Context,那么這個組件會從離自身最近的那個匹配的 Provider 中讀取到當前的context值;
defaultValue是組件在頂層查找過程中沒有找到對應的Provider,那么就使用默認值
theme-context.js
import React from "react"
// 1.創建一個Context
const ThemeContext = React.createContext({ color: "blue", size: 10 })
export default ThemeContext
user-context.js
import React from "react"
// 1.創建一個Context
const UserContext = React.createContext()
export default UserContext
Context.Provider
每個 Context 對象都會返回一個 Provider React 組件,它允許消費組件訂閱 context 的變化:
Provider 接收一個 value 屬性,傳遞給消費組件;
一個 Provider 可以和多個消費組件有對應關系;
多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數據;
當 Provider 的 value 值發生變化時,它內部的所有消費組件都會重新渲染;
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傳遞數據 */}
{/* <Home name="why" age={18}/>
<Home name={info.name} age={info.age}/>
<Home {...info}/> */}
{/* 2.普通的Home */}
{/* 第二步操作: 通過ThemeContext中Provider中value屬性為后代提供數據 */}
<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() 創建的 Context 對象:
這能讓你使用 this.context 來消費最近 Context 上的那個值;
你可以在任何生命周期中訪問到它,包括 render 函數中;
import React, { Component } from 'react'
import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'
export class HomeInfo extends Component {
render() {
// 4.第四步操作: 獲取數據, 并且使用數據
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 變更。這能讓你在 函數式組件 中完成訂閱 context。
這里需要 函數作為子元素(function as child)這種做法;
這個函數接收當前的 context 值,返回一個 React 節點;
什么時候使用Context.Consumer呢?
1.當使用value的組件是一個函數式組件時;
2.當組件中需要使用多個Context時;
import ThemeContext from "./context/theme-context"
function HomeBanner() {
return <div>
{/* 函數式組件中使用Context共享的數據 */}
<ThemeContext.Consumer>
{
value => {
return <h2> Banner theme:{value.color}</h2>
}
}
</ThemeContext.Consumer>
</div>
}
export default HomeBanner
我們什么使用setState
開發中我們并不能直接通過修改state的值來讓界面發生更新:
因為我們修改了state之后,希望React根據最新的State來重新渲染界面,但是這種方式的修改React并不知道數據發生了變化;
React并沒有實現類似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式來監聽數據的變化;
我們必須通過setState來告知React數據已經發生了變化;
疑惑:在組件中并沒有實現setState的方法,為什么可以調用呢?
原因很簡單,setState方法是從Component中繼承過來的。
setState異步更新
setState的更新是異步的?
最終打印結果是Hello World;
可見setState是異步的操作,我們并不能在執行完setState之后立馬拿到最新的state的結果
setState設計為異步,可以顯著的提升性能;
如果每次調用 setState都進行一次更新,那么意味著render函數會被頻繁調用,界面重新渲染,這樣效率是很低的;
最好的辦法應該是獲取到多個更新,之后進行批量更新;
如果同步更新了state,但是還沒有執行render函數,那么state和props不能保持同步;
state和props不能保持一致性,會在開發中產生很多的問題;
如何獲取異步的結果
式一:setState的回調
setState接受兩個參數:第二個參數是一個回調函數,這個回調函數會在更新后會執行;
格式如下:setState(partialState, callback)
changeText() {
this.setState({
message: '你好'
}), () => {
console.log(this.state.message)
}
}
當然我們也可以在生命周期函數
componentDidUpdate(precProps, provState, snapshot) {
console.log(this.state.message)
}
setState一定是異步的嗎(React18之前)
其實分成兩種情況:
在組件生命周期或React合成事件中,setState是異步;
在setTimeout或者原生dom事件中,setState是同步;
setState默認是異步的 (React18之后)
在React18之后,默認所有的操作都被放到了批處理中(異步處理)。
如果希望代碼可以同步會拿到,則需要執行特殊的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.網絡請求一: banners
// 2.網絡請求二: recommends
// 3.網絡請求三: 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被執行")
return (
<div>
<h2>message: {message}</h2>
<button onClick={e => this.changeText()}>修改文本</button>
<h2>當前計數: {counter}</h2>
<button onClick={e => this.increment()}>counter+1</button>
<Hello message={message}/>
</div>
)
}
}
export default App
原文鏈接:https://blog.csdn.net/weixin_65402230/article/details/128064037
相關推薦
- 2022-08-18 Flutter中關于angle的踩坑記錄_Android
- 2023-05-31 python升級pip及失敗處理方式_python
- 2023-05-10 Numpy中np.dot與np.matmul的區別詳解_python
- 2022-10-04 go常用指令之go?mod詳解_Golang
- 2022-05-26 解決redis批量刪除key值的問題_Redis
- 2022-07-12 利用python語言實現將Excel表格中的一列放進另一個Excel
- 2022-04-05 Pandas的DataFrame如何做交集,并集,差集與對稱差集_python
- 2022-06-24 Windows?Server?2012遠程默認端口3389的修改方法_win服務器
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支