react笔记——(2)组件通信与插槽
(注意:正常开发时会将不同组件进行多个文件分割,以下学习时将在一个文件中处理,方便学习)
1.组件通信(React DOM组件 + React组件)
1.1 DOM组件
DOM组件:指的是react支持的所有的HTML和SVG标签
1.1.1 为DOM组件设置 Props
1 2 3 4 5 6 7 8 9 10 11
| import image from './logo.svg' function App() { return ( <div> <img src={image} alt="" classname="small" /> </div> ) }
|
1.2 DOM组件的Style属性
1.2.1 直接设置style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import image from './logo.svg' function App() { return ( <div> <img src={image} alt="" classname="small" // 因为style 是一种键值的结构,所以JSX允许通过键值对(对象的形式)来进行style的设置 style={{ width: '100vh', height: 100, backgroundColor:'grey' }} /> </div> ) }
export default App;
|
1.2.2 书写变量再引入(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const imgStyleObj = { width: '100vh', height: 100, backgroundColor:'grey' }
return ( <div> <img src={image} alt="" classname="small" style={imgStyleObj} /> </div> )
|
1.3 JSX的展开语法
{…imgData}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import image from './logo.svg' function App() { const imgData = { className:"small", style:{ width: '100vh', height: 100, backgroundColor:'grey' } } return ( <div> <img src={image} alt="" {...imgData} /> </div> ) }
export default App;
|
注意: JSX的展开操作并不是ES6的展开运算符,ES6的展开运算符的功能是将当前对象的键值直接放到 {…imgData} 的位置上,它不能在一个没有容器的地方上单独使用的。
1 2 3 4 5 6 7 8 9
| return ( <div> <img src={image} alt="" {...imgData} //这里的 {} 只是JSX的语法标记,就算解开这个大括号,也不会直接将属性放在IMG标签之中(面试点) /> </div> )
|
2.React组件的Props(自定义的React组件)
2.1 有明确的键和值的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function Article (props){ 右边采用解构的方式,更加灵活简洁 function Article ({title,content}){ return ( | return ( <div> |---> <div> <h2>{props.title}</h2> 想用哪个属性用哪个,不必全部引入 <h2>{title}</h2> <p>{props.content}</p> <p>{content}</p> </div> </div> ) ) } }
export default function App() { return(
<> <Article title='标签1' content='内容1' /> <Article title='标签2' content='内容2' /> <Article title='标签3' content='内容3' /> </> ) }
|
2.2 没有明确的键和值的属性
例如:active(默认为true,没写就是false)表示呈现状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| function Article ({title,content,active}){ return ( <div> <h2>{title}</h2> <p>{content}</p> //此处做三元检测 <p>状态:{ active ? '显示中' : '已隐藏'}</p> </div> ) }
export default function App() { return(
<> <Article title='标签1' content='内容1' active /> <Article title='标签2' content='内容2' /> <Article title='标签3' content='内容3' active /> </> ) }
|
2.3 在React组件中展开 Props的使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| function Detail ({content,active}) { return ( <> <p>{content}</p> <p>状态:{ active ? '显示中' : '已隐藏'}</p> </> ) }
function Article ({title, detailData}){ return ( <div> <h2>{title}</h2> <Detail {...detailData}/> </div> ) }
export default function App() { const articleData = { title:'标签1', detailData: { content:'内容1', active:'true' } } return( <> //步骤二: 将acticleData以展开的方式注入Props(Article ) <Article {...articleData} /> </> ) }
|
3.将JSX作为Props传递(组件插槽)
React中将插槽功能称为将JSX作为Props传递给子组件,它被预定义了一个属性叫做children
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function List1 ({children}) { return( <ul> {children} </ul> ) }
function List2 ({children}) { return( <ul> {children} </ul> ) }
export default function App() { return ( <> <List1> <li>列表项1</li> <li>列表项1</li> <li>列表项1</li> </List1> <List2> <li>列表项2</li> <li>列表项2</li> <li>列表项2</li> </List2> </> ) }
|
3.1 向多个位置传递JSX
(注意,所有Props组件传递的数据都是单向的(只读的),不要尝试做修改)
(父组件 —> 子组件)(App —> List1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| function List1 ({children,title,footer=<div>默认底部</div>}) { return( <> <h2>{title}</h2> <ul> {children} </ul> {footer} </> ) }
export default function App() { return ( <> <List1 title='列表1' footer={<p>这是底部内容1</p>} > <li>内容1</li> <li>内容2</li> <li>内容3</li> </List1>
<List1 title='列表2' footer={<p>这是底部内容2</p>} > <li>内容A</li> <li>内容B</li> <li>内容C</li> </List1>
<List1 title='列表3' footer={<p>这是底部内容3</p>} > <li>内容U</li> <li>内容I</li> <li>内容O</li> </List1> </> ) }
|
4.子组件向父组件传值
4.1 简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import {useState} from "react"
function Detail () { const [status,setStatus] = useState(true) function handleClick () { setStatus(!status) } return ( <div> <button onClick={handleClick}>按钮</button> <p style={{ // block: 显示 // none: 不显示 display: status ? 'block' : 'none' }}>Detail的内容</p> </div> ) }
export default function App() { return ( <> <Detail
/> </> ) }
|
4.2 给子组件设置自定义属性
如果我们希望父组件接收子组件的状态的话,那我们就需要去给子组件设置一个自定义状态(自定义命名即可)。
(注意:这类属性并不像DOM属性一样可以通过键盘鼠标触发,而是通过代码来决定在某个时机触发)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import {useState} from "react" function Detail ({ onActive }) { const [status,setStatus] = useState(true) function handleClick () { setStatus(!status) onActive(status) } return ( <div> <button onClick={handleClick}>按钮</button> <p style={{ // block: 显示 // none: 不显示 display: status ? 'block' : 'none' }}>Detail的内容</p> </div> ) }
export default function App() { function handleActive (status) { console.log(status) } return ( <> <Detail //自定义 onActive属性 onActive={handleActive} /> </> ) }
|
除了父子之外,还有同级组件之间的传值,同级组件传值最常用的办法就是借助父组件来进行中转,此处不过多演示。
5.使用Context进行多级组件传值
当需要进行多级传递时,再使用Props就比较麻烦了,这时使用React中的Hooks==》Context来进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
import { useState } from "react"
export function Section({ children }) { return ( <section className="section"> {children} </section> ) }
export function Heading({ level, children }) { switch (level) { case 1: return <h1>{children}</h1> case 2: return <h2>{children}</h2> case 3: return <h3>{children}</h3> case 4: return <h4>{children}</h4> case 5: return <h5>{children}</h5> case 6: return <h6>{children}</h6> default: throw Error("未知的level:" + level) } } export default function App() { return ( <div> <Section> <Heading level={1}>主标题</Heading> <Section> <Heading level={2}>子标题</Heading> <Heading level={2}>子标题</Heading> <Heading level={2}>子标题</Heading> <Section> <Heading level={3}>子子标题</Heading> <Heading level={3}>子子标题</Heading> <Heading level={3}>子子标题</Heading> <Section> <Heading level={4}>子子子标题</Heading> <Heading level={4}>子子子标题</Heading> <Heading level={4}>子子子标题</Heading> </Section> </Section> </Section> </Section> </div> ) }
|
(面试小点)
通过上面这张图,我们可以看到虽然都是 h1 标签,但是仍旧呈现出了 h1h5 的这种样式,这是因为在使用 section与标题标签(h1h5) 嵌套时会自然呈现出的状态。
所以我们经常说 HTML是做结构呈现的,CSS才是做样式处理的,所以即使使用的HTML标签默认尺寸符合我们要求,我们依旧要进行设置限制。
那么接下来使用react提供的钩子(createContext、useContext、createContext.Provider)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import { createContext, useContext} from "react"
export function Section({ children }) { const level = useContext(LevelContext) return ( <section className="section"> <LevelContext.Provider value={level + 1}> {children} </LevelContext.Provider> </section> ) }
export function Heading({children }) { const level = useContext(LevelContext) switch (level) { case 1: return <h1>{children}</h1> case 2: return <h2>{children}</h2> case 3: return <h3>{children}</h3> case 4: return <h4>{children}</h4> case 5: return <h5>{children}</h5> case 6: return <h6>{children}</h6> default: throw Error("未知的level:" + level) } }
const LevelContext = createContext(0) export default function App() { return ( <div> <Section> <Heading >主标题</Heading> <Section> <Heading >子标题</Heading> <Heading >子标题</Heading> <Heading >子标题</Heading> <Section> <Heading >子子标题</Heading> <Heading >子子标题</Heading> <Heading >子子标题</Heading> <Section> <Heading >子子子标题</Heading> <Heading >子子子标题</Heading> <Heading >子子子标题</Heading> </Section> </Section> </Section> </Section> </div> ) }
|