React 18 组件通信与插槽

332 阅读3分钟

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;