Skip to content

React 文档

导读与全书目录表:见同目录 01-React 入门:简介·特性与设计范式.md 开头。


react 函数组件通信

父子通信

  1. 父 -> 子通信(props):父动态属性绑定参数,子函数组件形参为 props
js
// 父组件
function ParentComponent() {
  const message = 'Hello from parent';
  return <ChildComponent greeting={message} />;
}

// 子组件
function ChildComponent(props) {
  return <h1>{props.greeting}</h1>;
}
  1. 子 -> 父通信(回调函数):父动态属性绑定参数值为回调函数,子 props.动态属性 调用
js
// 父组件
function ParentComponent() {
  const handleChildData = (data) => {
    console.log('Data from child:', data);
  };

  return <ChildComponent onSendData={handleChildData} />;
}

// 子组件
function ChildComponent(props) {
  const sendData = () => {
    props.onSendData('Data from child');
  };

  return <button onClick={sendData}>Send Data</button>;
}
  1. 父 -> 子通信(Refs):获取并操作子组件实例的属性和方法(不推荐):forwardRef + useImperativeHandle
js
import React, { useRef, forwardRef, useImperativeHandle } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const internalRef = useRef();

  useImperativeHandle(ref, () => ({
    // 暴露给父组件的方法
    focus: () => {
      internalRef.current.focus();
    },
    // 暴露给父组件的属性
    value: internalRef.current?.value,
  }));

  return <input ref={internalRef} />;
});

function ParentComponent() {
  const childRef = useRef(null);

  const handleClick = () => {
    childRef.current.focus(); // 调用子组件暴露的方法
    console.log(childRef.current.value); // 访问子组件暴露的属性
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleClick}>聚焦输入框</button>
    </div>
  );
}

后代传参

  1. 方式一:使用 Context API
js
import React, { useContext } from 'react';

const MyContext = React.createContext();

function App() {
  return (
    <MyContext.Provider value={{ theme: 'dark' }}>
      <Parent />
    </MyContext.Provider>
  );
}

function Parent() {
  return <Child />;
}

function Child() {
  const context = useContext(MyContext);
  return <div>Theme: {context.theme}</div>;
}
  1. 方式二:使用 props 不断向下传递

公共状态管理

redux & redux-thunk & react-redux

注意

  • ref 变化不会触发组件重新渲染
  • 函数组件本身没有实例,所以 ref 只能指向 DOM 元素或使用 useImperativeHandle 暴露特定值,如果子组件是类组件,ref 会指向组件实例,可以访问其方法和 state

react 类组件通信

父子通信

  1. 父 -> 子通信(props):父动态属性绑定参数,子this.props 接收
js
// 父组件
class ParentComponent extends React.Component {
  render() {
    return <ChildComponent message="Hello from Parent" />;
  }
}

// 子组件
class ChildComponent extends React.Component {
  render() {
    return <div>{this.props.message}</div>;
  }
}
  1. 子 -> 父通信(回调函数):父动态属性绑定参数值为回调函数,子 this.props.动态属性 调用
js
// 父组件
class ParentComponent extends React.Component {
    handleChildData = (data) => {
        console.log('Data from child:', data);
    };

    render() {
        return <ChildComponent sendData={this.handleChildData} />;
    }
}

// 子组件
class ChildComponent extends React.Component {
    sendDataToParent = () => {
        this.props.sendData('Data from Child');
    };

    render() {
        return <button onClick={this.sendDataToParent}>Send Data</button>;
    }
}
  1. 父 -> 子通信(Refs):获取并操作子组件实例的属性和方法
js
class ParentComponent extends React.Component {
    constructor(props) {
        super(props);
        this.childRef = React.createRef();
    }

    callChildMethod = () => {
        this.childRef.current.childMethod('Data from Parent');
    };

    render() {
        return (
            <div>
                <button onClick={this.callChildMethod}>Call Child Method</button>
                <ChildComponent ref={this.childRef} />
            </div>
        );
    }
}

class ChildComponent extends React.Component {
    childMethod = (data) => {
        console.log(data);
    };

    render() {
        return <div>Child Component</div>;
    }
}

后代传参

  1. 方式一:使用 Context API
js
// 创建Context
const MyContext = React.createContext();

// 提供者组件
class ParentComponent extends React.Component {
  state = {
    value: 'Context Value'
  };

  render() {
    return (
      <MyContext.Provider value={this.state.value}>
        <ChildComponent />
      </MyContext.Provider>
    );
  }
}

// 消费者组件
class ChildComponent extends React.Component {
  static contextType = MyContext;

  render() {
    return <div>{this.context}</div>;
  }
}
  1. 方式二:使用 props 不断向下传递

公共状态管理

redux & redux-thunk & react-redux

注意

  • ref 变化不会触发组件重新渲染
  • 函数组件本身没有实例,所以 ref 只能指向 DOM 元素或使用 useImperativeHandle 暴露特定值,如果子组件是类组件,ref 会指向组件实例,可以访问其方法和 state

说说 react 组件通信?表达要点

回答

shell
# 父子通信
父组件 子组件:通过 props 传递,父组件把数据 / 方法通过属性传给子组件,子组件用 props 接收
子组件 父组件:通过 props 回调函数传递,子组件调用父组件传过来的函数,把数据当参数传回去。

# 跨层级 / 兄弟组件:
方案一:状态提升,把共享状态提到最近公共父组件,由父组件统一管理,再通过 props 分发给子组件。
方案二:Context API 适合多层嵌套、跨很多层级的场景,避免 props 层层传递(props drilling)
方案三:全局复杂状态 通过 Redux / Redux Toolkit 状态管理库,统一维护全局状态

# 其他
ref 通信:父组件获取子组件实例 / DOM,调用子组件方法。
事件总线(event bus):简单项目用发布订阅。EventBus(事件总线,比如监听全局自定义事件)

# 最后
实际开发中,Context API 适合小范围共享(比如主题),Redux 适合大规模全局状态,避免过度使用 Context 导致组件重渲染范围过大。

react 插槽

简单内容传递:使用 children

多个命名区域:使用 props 传递 JSX

动态内容或复杂交互:Context API

需要操作子组件:使用 React.Children API

普通插槽(props.children)

js
// 子组件
function Card({ children }) {
  return <div className="card">{children}</div>;
}

// 父组件
function App() {
  return (
    <Card>
      <h2>标题</h2>
      <p>内容...</p>
    </Card>
  );
}

命名插槽(props 传递)

js
// 子组件
function Layout({ header, sidebar, content }) {
  return (
    <div className="layout">
      <div className="header">{header}</div>
      <div className="sidebar">{sidebar}</div>
      <div className="content">{content}</div>
    </div>
  );
}

// 父组件
function App() {
  return (
    <Layout
      header={<h1>网站标题</h1>}
      sidebar={<ul><li>菜单1</li><li>菜单2</li></ul>}
      content={<p>主要内容区域...</p>}
    />
  );
}

高级插槽(Context API)

js
const SlotContext = React.createContext();

function SlotProvider({ children, slots }) {
  return (
    <SlotContext.Provider value={slots}>
      {children}
    </SlotContext.Provider>
  );
}

function Slot({ name }) {
  const slots = React.useContext(SlotContext);
  return slots[name] || null;
}

function App() {
  return (
    <SlotProvider slots={{
      header: <h1>自定义标题</h1>,
      footer: <footer>页脚内容</footer>
    }}>
      <div>
        <Slot name="header" />
        <main>主要内容</main>
        <Slot name="footer" />
      </div>
    </SlotProvider>
  );
}