React 18 组件通信与插槽
React 中的组件分为 DOM 组件和自定义组件两种类型。
一、DOM 组件传值
DOM 组件指的是 React 支持的所有的 HTML 和 SVG 标签。在 React 中,HTML 属性并不是纯 HTML 写法,会有一些不同,这些 HTML 属性在 React 中称为 props。比如:
-
为了跟 JS 中的
class关键字作区分,需要用className来设置 css 类名。import image from './logo.svg' function App() { return ( <div> <img src={image} alt="" className="small" /> </div> ); } export default App; -
使用对象的形式进行内联样式的设置。
import image from './logo.svg' function App() { return ( <div> <img src={image} alt="" className="small" style={{ width: '200px', height: '200px', backgroundColor: 'green' }} /> </div> ); } export default App; -
使用 JSX 的展开操作为 HTML 标签添加属性
import image from './logo.svg' function App() { const imgData = { className: 'small', style: { width: '200px', height: '200px', backgroundColor: 'green' } } return ( <div> <img src={image} alt="" {...imgData} /> </div> ); } export default App;小贴士:JSX 的展开操作并不是 ES6 的展开运算符哦~
二、自定义组件传值
父组件向子组件传值
通过设置 props 可以实现父组件向子组件传值(DOM 上的属性和自定义组件上的属性都叫做 props)。
-
传递普通值
function Article({title, content, active}) { return ( <div> <h1>{title}</h1> <p>{content}</p> <p>状态:{active ? '显示中' : '已隐藏'}</p> </div> ) } function App() { return ( <> <Article title="标题1" content="内容1" active /> <Article title="标题2" content="内容2" /> <Article title="标题3" content="内容3" /> </> ); } export default App;同样的,也可以使用 JSX 的展开操作为自定义组件添加属性:
function Detail({content, active}) { return ( <div> <p>{content}</p> <p>状态:{active ? '显示中' : '已隐藏'}</p> </div> ) } function Article({title, detailData}) { return ( <div> <h1>{title}</h1> <Detail {...detailData}/> </div> ) } function App() { const articleData = { title: '标题1', detailData: { content: '内容1', active: true } } return ( <> <Article {...articleData}/> </> ); } export default App; -
传递 JSX(组件插槽)
-
子组件可以直接通过预定义字段
children接收默认位置的 JSX。function List({children}) { return ( <ul> {children} </ul> ) } function App() { return ( <> <List> <li>列表项</li> <li>列表项</li> <li>列表项</li> </List> </> ); } export default App; -
设置子组件 props 传递 JSX
function List({children, title, footer = <div>默认底部</div>}) { return ( <> <h1>{title}</h1> <ul> {children} </ul> {footer} </> ) } function App() { return ( <> <List title="列表1" footer={<p>这是底部内容1</p>} > <li>列表项</li> <li>列表项</li> <li>列表项</li> </List> <List title="列表2" > <li>列表项</li> <li>列表项</li> <li>列表项</li> </List> </> ); } export default App;function List({children, title, footer = <div>默认底部</div>}) { return ( <> <h1>{title}</h1> <ul> {children} </ul> {footer} </> ) } function App() { return ( <> <List title="列表1" footer={<p>这是底部内容1</p>} > <li>列表项</li> <li>列表项</li> <li>列表项</li> </List> <List title="列表2" > <li>列表项</li> <li>列表项</li> <li>列表项</li> </List> </> ); } export default App;
-
子组件向父组件传值
子组件通过调用父组件传递进来的函数,来实现子组件向父组件传值。
import { useState } from "react";
function Detail({onActive}) {
const [status, setStatus] = useState(false)
function handleClick() {
setStatus(!status)
onActive(status)
}
return (
<>
<button onClick={handleClick}>按钮</button>
<p style={{display: status ? 'block' : 'none'}}>Detail的内容</p>
</>
)
}
function App() {
function handleActive(status) {
console.log(status);
}
return (
<>
<Detail onActive={handleActive}/>
</>
);
}
export default App;
跨级组件传值
Context API 是跨组件层级传递数据的一种有效方式。它允许你创建一个数据容器,该容器可以被树中的任何组件访问,而无需在每一层手动传递 props。
使用步骤:
- 创建一个 Context:使用
createContext()。 - 提供 Context 值:使用
<YourContext.Provider value={/* some value */}>包裹组件树。 - 使用 Context 值:在需要访问这个值的组件中,使用
useContext()。
示例代码:
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext('dark') // 注意命名首字母都需要大写
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button>{theme === 'dark' ? 'Dark Theme' : 'Light Theme'}</button>;
}
function Toolbar() {
return (
<div>
<ThemeButton />
</div>
);
}
function App() {
const [theme, setTheme] = useState('dark')
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
export default App;