记 18 React进阶用法

90 阅读4分钟

React建议在开发中将不同组件进行多个文件的分割,但为方便学习,这里将结构都在同一个文件(App.js)中展示。

组件通信

父子组件传值(props)都是只读的 React中的组件分为两种,

  • ReactDOM组件 DOM是指React支持的所有的html和svg标签

DOM组件中的Props设置

JSX支持传统属性设置,但也存在一部分属性使用方式的变更

类名className(与js语法的class做区分)

样式style

设置一

function App() {
  return (
    <>
      <img src={image} alt='' style={{
        width: 200,//两种写法等价
        height: '200px',
        objectFit: 'cover'
      }} />
    </>
  );
}

设置二

function App() {
  const imageStyle = {
    width: 200,
    height: '200px'
  }
  return (
    <>
      <img src={image} alt='' style={imageStyle} />
    </>
  );
}

JSX的展开语法

当我们需要给标签设置一个属性对象集合时,使用展开语法{ ...myData }直接展开

function App() {
  const myData = {
    src: image,
    style: {
      width: 200,
      height: '200px'
    },
  }
  return (
    <>
      <img alt='' {...myData} />
    </>
  );
}

React组件

对如下父子组件

function Article() {
  return (
    <div>
      <h1>Hello World</h1>
      <p>Hello content</p>
    </div>
  );

}
export default function App() {
  return (
    <>
      <Article />
      <Article />
      <Article />
    </>
  );
}

React组件父组件传值给子组件

当我们需要对其样式逻辑进行复用时,有以下步骤

  1. 请求服务端提供功能所需数据
  2. 创建子组件
  3. 将数据传递给子组件进行渲染
function Article(props) {//也可以直接解构出props的属性
// function Article({titele,content,active}) {
  return (
    <div>
      <h1>{props.title}</h1>
      <p>{props.content}</p>
      {props.active?<p>显示中</p>:<p>已隐藏</p>}
    </div>
  );

}
export default function App() {
  return (
    <>
      <Article title='标签1' content='内容1' active/>
      <Article title='标签2' content='内容2'/>
      <Article title='标签3' content='内容3'/>
    </>
  );
}

React组件嵌套传值及列表渲染

import React, { Fragment } from 'react';

function Detail({ content, active }) {
  return (
    <>
      <p>{content}</p>
      {active ? <p>显示中</p> : <p>已隐藏</p>}
    </>
  );

}
function Article({ title, detailData }) {
  return (
    <>
      <h1>{title}</h1>
      <Detail {...detailData} />
    </>
  );

}
export default function App() {
  const articleList = [
    {
      id: 1,
      title: '标签1',
      detailData: {
        content: '内容1',
        active: true
      }
    },
    //...省略了
  ];

  const articleData = articleList.map(item => (
    //JSX中只能有一个根元素,所以需要用一个空标签包裹
    //如果循环中每次都存在多个根元素,那么就需要使用Fragment
    <Fragment key={item.id}>
      <Article {...item} />
    </Fragment>
  ))

  return (
    <>
      {articleData}
    </>
  );
}

将JSX作为Props传递(组件插槽)

React中预定义了属性children(组件开始与结束标记之间的所有内容)

向多个位置传递JSX
function List({ children, title, footer = <div>默认底部</div> }) {
  return (
    <>
      <h2>{title}</h2>
      <ul>{children}</ul>
      {footer}
    </>
  );
}
export default function App() {
  return (
    <>
      <List title="列表1">
        <li>列表项1</li>
        <li>列表项2</li>
        <li>列表项3</li>
      </List>
      <List title="列表2" footer={<p>自定义底部</p>}>
        <li>列表项1</li>
        <li>列表项2</li>
        <li>列表项3</li>
      </List>
    </>
  );
}

最佳方案: 将列表在app中处理成一个数组包含对象的形式,更利于代码的处理

React组件子组件传值给父组件

通过父组件给子组件设置自定义事件,自定义事件触发后,向父组件传递参数

import React, { useState } from 'react';
function Detail({ onActive }) {
  const [status, setStatus] = useState(true);//通过useState创建的状态来控制
  function handleClick() { //点击事件被触发
    setStatus(!status);
    onActive(status);//调用父组件的方法,将参数传过去
  }
  return (
    <div>
      <button onClick={handleClick}>按钮</button>
      <h1 style={{
        display: status ? "block" : "none"
      }}>Detail</h1>
    </div>
  );
}
export default function App() {
  function handler(status) {//子组件中被触发的函数调用该函数将数据传回
    console.log(status,"我是传给父组件的参数");
  }
  return (
    <>
      <Detail
        onActive={handler} //绑定自定义事件
      />
    </>
  );
}

React组件中同级组件传值context

常用方式是用父组件进行中转(子传父=>父传子 = 兄弟传值)

React组件中多级组件传值hooks: context

在根组件中声明一个Context,在子组件中使用useContext获取当前的level值,然后根据level值渲染对应的标题。 levelContext.Provider 接受的属性 value 可以是从上一级组件传递下来的值,也可以是createContext设置的默认值。value + 1 后传递给子组件return 出去

import { createContext, useContext } from "react";

function Section({ children }) {
  const level = useContext(levelContext);// 获取当前的level值
  // 渲染子组件,传递下一级的level值
  return (
    <section className="section">
      <levelContext.Provider value={level + 1}>
        {children}
      </levelContext.Provider>
    </section>
  );
}

function Heading({ children }) {
  const level = useContext(levelContext);// 获取当前的level值
  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:
      return console.error("Invalid level value");
  }
}

//在根组件中声明一个Context,默认值为1,在子组件中使用useContext获取当前的level值,然后根据level值渲染对应的标题。
const levelContext = createContext(1);
export default function App() {
  return (
    <>
      <Section>
        <Heading>主标题</Heading>

        <Section>
          <Heading>副标题</Heading>
          <Heading>副标题</Heading>
          <Heading>副标题</Heading>
          <Section>
            <Heading>子标题</Heading>
            <Heading>子标题</Heading>
            <Section>
              <Heading>子子标题</Heading>
              <Section>
                <Heading>子子子标题</Heading>
                <Section>
                  <Heading>子子子子标题</Heading>
                </Section>
              </Section>
            </Section>
          </Section>
        </Section>
      </Section>
    </>
  );
}