一文吃透 React 核心基础:从表单绑定到组件通信

366 阅读6分钟

在 React 开发的世界里,掌握基础才能搭建出稳固的应用架构。今天我们就深入探讨 React 中表单受控绑定、DOM 获取以及组件通信的多种方式,通过详细代码示例和通俗易懂的讲解,帮助大家快速掌握这些核心技能。

表单受控绑定:让输入尽在掌控

在 React 中,表单的受控绑定是实现数据双向绑定的关键。与传统 HTML 表单不同,React 表单的输入值是由状态控制的,这就是所谓的 “受控组件”。

实现表单受控绑定的步骤非常清晰:

  1. 声明 React 状态:使用 useState 钩子函数创建一个状态来存储表单输入的值。useState 会返回一个数组,第一个元素是当前状态值,第二个元素是用于更新状态的函数。例如:const [value, setValue] = useState(''); 这里创建了一个初始值为空字符串的状态 value 和更新它的函数 setValue。
  1. 核心绑定流程
    • 通过 value 属性将表单元素(如 input)与 React 状态进行绑定。这样,表单元素显示的内容始终与状态值保持一致。
    • 绑定 onChange 事件,在事件回调函数中,通过事件参数 e 获取输入框的最新值,并使用 setValue 函数将其更新到 React 状态中。

来看一个完整的示例:

import { useState } from'react';
function App() {
    const [value, setValue] = useState('');
    return (
        <div className="App">
            <input type="text" value={value} onChange={(e) => {
                setValue(e.target.value);
            }} />
        </div>
    );
}

在这个例子中,每次输入框内容发生变化,onChange 事件就会触发,setValue 函数更新 value 状态,从而保证输入框显示的内容始终和 value 状态同步,实现了数据的双向绑定。

React 中获取 DOM:精准定位页面元素

在 React 中,虽然推荐通过状态和 props 来驱动 UI,但有时我们也需要直接操作 DOM,比如获取元素的尺寸、聚焦输入框等。这时就可以使用 useRef 钩子函数来获取 DOM 对象。

获取 DOM 的步骤如下:

  1. 使用 useRef 生成一个 ref 对象,并将其绑定到目标 DOM 标签上。useRef 会返回一个可变的 ref 对象,其 .current 属性初始值为传入的参数(通常设为 null)。例如:const inputRef = useRef(null);
  1. 当 DOM 可用时(一般在组件渲染完毕后),通过 ref.current 就可以获取到对应的 DOM 对象。需要注意的是,ref.current 在组件首次渲染时可能为 null,因为此时 DOM 还未完全生成。

下面是一个获取输入框 DOM 的示例:

import { useRef } from'react';
function App() {
    const inputRef = useRef(null);
    const showDom = () => {
        console.log(inputRef.current);
        console.dir(inputRef.current);
    }
    return (
        <div>
            <input type='text' ref={inputRef}></input>
            <button onClick={showDom}>获取 dom</button>
        </div>
    )
}

点击 “获取 dom” 按钮时,showDom 函数会通过 inputRef.current 获取到输入框的 DOM 元素,并在控制台打印出来,方便我们查看和操作。

组件通信:搭建数据传递桥梁

在 React 应用中,组件之间的通信至关重要,它让不同组件协同工作,实现复杂的功能。组件通信主要有以下几种方式:

父传子:从上至下的信息传递

父组件向子组件传递数据是最常见的通信方式。实现步骤如下:

  1. 父组件传递数据:在父组件中,在子组件标签上绑定属性,将需要传递的数据作为属性值。
  1. 子组件接受数据:子组件通过 props 参数来接收父组件传递过来的数据。

props 有两个重要特性:

  • props 可以传递任何类型的数据,包括字符串、数字、数组、对象,甚至是函数。
  • props 是只读的,子组件不能直接修改 props。当父组件的状态或数据发生变化时,子组件会自动重新渲染以反映这些变化。

另外,当在子组件标签中嵌套内容时,父组件会自动将这些内容添加到子组件 props 的 children 属性上。

来看一个简单的示例:

function Son(props) {
    console.log(props);
    return <div>this is son,{props.name}</div>
}
function App() {
    const name = 'this is app name';
    return (
        <div>
            <Son name={name} />
        </div>
    );
}
export default App;

在这个例子中,父组件 App 将 name 数据通过 name 属性传递给子组件 Son,子组件通过 props.name 来展示接收到的数据。

子传父:自下而上的信息反馈

子组件向父组件传递数据通常是通过回调函数来实现的。具体步骤如下:

  1. 父组件定义一个回调函数,并将其作为 props 传递给子组件。
  1. 子组件在合适的时机(比如用户点击按钮时)调用这个回调函数,并将需要传递的数据作为参数传入。

看下面的代码示例:

import React, { useState } from'react';
function Son(props) {
    const { onGetSonMsg } = props;
    const sonMsg ='son msg';
    return (
        <div>
            this is son
            <button onClick={() => onGetSonMsg(sonMsg)}>get son msg</button>
        </div>
    )
}
function App() {
    const [msg, setMsg] = useState('');
    const getMsg = (msg) => {
        setMsg(msg);
    }
    return (
        <div>
            <Son onGetSonMsg={getMsg} name="son"/>
            <p>{msg}</p>
        </div>
    );
}

在这个例子中,子组件 Son 在按钮点击时调用父组件传递过来的 onGetSonMsg 函数,并将 sonMsg 作为参数传入,父组件通过 getMsg 函数更新状态 msg,从而在页面上展示子组件传递过来的数据。

兄弟组件通信:状态提升实现数据共享

兄弟组件之间的通信通常需要借助它们的共同父组件,也就是 “状态提升”。具体流程如下:

  1. 通过子传父:其中一个兄弟组件(比如组件 A)将数据传递给父组件。
  1. 通过父传子:父组件再将数据传递给另一个兄弟组件(组件 B)。

示例代码如下:

import React, { useState } from'react';
function A({ onGetAName }) {
    const name = 'this is A name';
    return (
        <div>
            this is A
            <button onClick={() => onGetAName(name)}>send</button>
        </div>
    );
}
function B(props) {
    const { name } = props;
    return (
        <div>
            this is B
            <p>{name}</p>
        </div>
    );
}
function App() {
    const [name, setName] = useState('');
    const getAName = (name) => {
        setName(name);
    }
    return (
        <div>
            this is App
            <A onGetAName={getAName} />
            <B name={name} />
        </div>
    );
}

在这个示例中,组件 A 将数据 name 通过 onGetAName 函数传递给父组件 App,父组件更新状态 name 后,再通过 name 属性将数据传递给组件 B,实现了兄弟组件之间的数据通信。

使用 Context 实现跨层组件通信:打破组件层级限制

当组件层级较深,数据需要在多层嵌套的组件之间传递时,使用 Context 可以避免繁琐的逐层传递 props。

使用 Context 的步骤如下:

  1. 使用 createContext 方法创建一个上下文对象。例如:const MsgContext = createContext();
  1. 在顶层组件中,通过 Provider 组件提供数据,将需要共享的数据作为 value 属性传入。
  1. 在底层组件中,使用 useContext 钩子函数获取并使用共享的数据。

看下面的代码示例:

import { createContext, useContext } from'react';
const MsgContext = createContext();
function A({ onGetAName }) {
    return (
        <div>
            this is A
            <B />
        </div>
    );
}
function B() {
    const msg = useContext(MsgContext);
    return (
        <div>
            this is B  {msg}
        </div>
    );
}
function App() {
    const msg = 'this is msg';
    return (
        <div>
            <MsgContext.Provider value={msg}>
                this is App
                <A />
            </MsgContext.Provider>
            this is App
            <A />
        </div>
    );
}
export default App;

在这个例子中,顶层组件 App 通过 MsgContext.Provider 提供数据 msg,底层组件 B 直接使用 useContext(MsgContext) 获取到共享的数据并进行展示,无需通过中间层层组件传递,大大简化了跨层组件通信的过程。

掌握了这些 React 的核心基础知识,我们就能更加灵活地构建 React 应用,实现各种复杂的交互和功能。希望本文能对大家的学习和开发有所帮助!