Skip to content

❤ React-组件通讯

组件通讯将教我们的内容:

  • 能够使用道具接收数据
  • 能够实现父子组件之间的通讯
  • 能够实现兄弟组件之间的通讯
  • 能够给组件添加道具校验
  • 能够说出生命周期常用的钩子函数
  • 能够知道高阶组件的作用

1、 组件通讯介绍

组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。

在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要共享某些数据为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯。

换个意思说就是,大家之间总是需要沟通的呗

2、 组件的props

组件是封闭的,要接收外部数据应该通过 props 来实现props的作用:接收传递给组件的数据传递数据:给组件标签添加属性 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据

类组件和函数式组件传递参数进行通讯如下图所示:(推荐函数式组件)

函数式组件的方式:

js
import React from 'react';

function Hello(props) {
  // 输出接收到的 props
  console.log(props);

  // 返回 JSX 元素
  return (
    <div>接收到数据: {props.name}, 年龄: {props.age}</div>
  );
}

// 使用 Hello 组件并传递 props
function App() {
  return (
    <Hello name="jack" age={19} />
  );
}

export default App;

类组件的方式:

js
import React from 'react';

class Hello extends React.Component {
  render() {
    return (
      <div>接收到的数据: {this.props.age}</div>
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <Hello name="jack" age={19} />
      </div>
    );
  }
}

export default App;

函数组件props

使用案例方法

js
import React from 'react';
import ReactDOM from 'react-dom';

// 1. 接收数据的 Hello 组件
const Hello = (props) => {
  // props 是一个对象
  console.log(props);
  return (
    <div>
      <h1>props: {props.name}</h1>
    </div>
  );
}

// 2. 传递数据并渲染组件
ReactDOM.render(<Hello name="jack" age={19} />, document.getElementById('root'));

类组件this.props

js
import React from 'react';
import ReactDOM from 'react-dom/client';

class Hello extends React.Component {
  render() {
    // 打印传入的 props
    console.log(this.props);
    return (
      <div>
        <h1>props: {this.props.name}</h1>
      </div>
    );
  }
}

// 在 React 18 中使用 createRoot
const root = ReactDOM.createRoot(document.getElementById('root'));

// 渲染 Hello 组件并传递 props
root.render(<Hello name="jack" age={18} />);

组件props三个特点:

① 可以给组件传递任意类型的数据

② props是只读的对象,只能读取属性的值,无法修改对象

③ 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取props

  1. 可以给组件传递任意类型的数据

  2. props 是只读的对象,只能读取属性的值,无法修改对象

  3. 注意:使用类组件时,如果写了构造函数,应该将 props 传递给 super(),否则,无法在构造函数中获取到 props !

js
import React from 'react';

class Hello extends React.Component {
  // 构造函数接收 props,并传递给父类构造函数
  constructor(props) {
    super(props); // 推荐将 props 传递给父类构造函数
  }

  render() {
    // 使用 this.props 访问传递的数据
    return <div>接收到的数据: {this.props.age}</div>;
  }
}

// 使用 ReactDOM 来渲染组件,假设我们有一个 ID 为 'root' 的元素
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));

// 渲染 Hello 组件并传递 props
root.render(<Hello age={18} />);

3、 组件通讯的三种方式

父子、子父、兄弟组件

js
import React from 'react';
import ReactDOM from 'react-dom';

// 父组件
class Parent extends React.Component {
  // 定义父组件的 state
  state = {
    lastName: '王',
  };

  render() {
    return (
      <div className="parent">
        父组件:
        {/* 传递 lastName 给子组件 */}
        <Child name={this.state.lastName} />
      </div>
    );
  }
}

// 子组件
const Child = (props) => {
  console.log('子组件:', props); // 打印接收到的 props
  return (
    <div className="child">
      <p>子组件,接收到父组件的数据: {props.name}</p>
    </div>
  );
};

// 渲染父组件
ReactDOM.render(<Parent />, document.getElementById('root'));

(1)传递方式1

  1. 父组件提供要传递的state数据

  2. 给子组件标签添加属性,值为 state 中的数据

  3. 子组件中通过 props 接收父组件中传递的数据

js
import React from 'react';
import ReactDOM from 'react-dom';

// 父组件
class Parent extends React.Component {
  // 定义父组件的 state
  state = {
    lastName: '王', // 设置lastName的初始值
  };

  render() {
    return (
      <div>
        传递数据给子组件:
        {/* 传递 state 中的 lastName 给子组件 */}
        <Child name={this.state.lastName} />
      </div>
    );
  }
}

// 子组件
function Child(props) {
  return <div>子组件接收到数据: {props.name}</div>;
}

// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));

(2)传递方式2

子组件传递数据给父组件

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数

  1. 父组件提供一个回调函数(用于接收数据)
  2. 将该函数作为属性的值,传递给子组件
js
import React from 'react';
import ReactDOM from 'react-dom';

// 父组件
class Parent extends React.Component {
  // 父组件方法,接收子组件数据
  getChildMsg = (msg) => {
    console.log('接收到子组件数据', msg);
  };

  render() {
    return (
      <div>
        子组件:
        {/* 将父组件的方法传递给子组件 */}
        <Child getMsg={this.getChildMsg} />
      </div>
    );
  }
}

// 子组件
function Child(props) {
  // 模拟子组件发送消息
  const sendMessage = () => {
    props.getMsg('来自子组件的消息');
  };

  return (
    <div>
      <button onClick={sendMessage}>点击发送消息给父组件</button>
    </div>
  );
}

// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。

3.子组件通过 props 调用回调函数

js
import React from 'react';
import ReactDOM from 'react-dom';

// 子组件
class Child extends React.Component {
  // 定义子组件的 state
  state = {
    childMsg: 'React',
  };

  // 处理点击事件,向父组件传递数据
  handleClick = () => {
    this.props.getMsg(this.state.childMsg);  // 调用父组件传递的 getMsg 方法
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我,给父组件传递数据</button>
      </div>
    );
  }
}

// 父组件
class Parent extends React.Component {
  // 父组件接收子组件的数据
  getChildMsg = (msg) => {
    console.log('接收到子组件的数据:', msg);
  };

  render() {
    return (
      <div>
        <h1>父组件</h1>
        {/* 将父组件的方法传递给子组件 */}
        <Child getMsg={this.getChildMsg} />
      </div>
    );
  }
}

// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));

案例

js
import React from 'react';
import ReactDOM from 'react-dom/client'; //React 18 
import './index.css'
class Parent extends React.Component{
    state={
        lastName:'父亲',
    }
    getChildMsg=(msg)=>{
        console.log('接收到子组件数据',msg);  
        this.setState({
            lastName:msg
        })
    }
    render(){
        return (
        <div className='parent'>
             父组件:
             {this.state.lastName}
             <Child getMsg={this.getChildMsg}></Child>
        </div>)
    }
}
class Child extends React.Component{
    state={
        msg:'行为'
    }
    handleClick=()=>{
        this.props.getMsg(this.state.msg);
    }
    render(){
        return (
            <div className='child'>
              子组件:<button onClick={this.handleClick}> 传递数据给付组件</button>
            </div>) 
    }
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Parent name='jack' age={18} colors={['red','green','blue']} 
fn={()=>{console.log('这是一个组件传递函数')}} tag={<p>设置一个p标签</p>}/>);

(3)兄弟组件通讯

image.png

(4)context

image.png

使用步骤:

1.调用 React. createContext() 创建 Provider(提供数据)和 Consumer(消费数据)两个组件。

js
const { Provider, Consumer }= React.createContext()

2.使用 Provider 组件作为父节点。

js
<Provider>
    <div className="App">
        <Child1 />
    </div>
</Provider>

3.设置 value 属性,表示要传递的数据。

js
<Provider value="pink">
    <div className="App">
        <Child1 />
    </div>
</Provider>

4.在子组件中通过 Consumer 组件接收数据。

js
<Consumer>
    {data => <span>data参数表示接收到的数据--{data}</span>}
</Consumer>

过程:

  1. 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
  2. Context提供了两个组件:Provider和 Consumer
  3. Provider组件:用来提供数据
  4. Consumer组件:用来消费数据

案例:

js
import React from 'react';
import ReactDOM from 'react-dom';

// 创建 Context
const { Provider, Consumer } = React.createContext();

// 子组件
class Node extends React.Component {
  render() {
    return (
      <Consumer>
        {(value) => <div style={{ color: value }}>这是 Node 组件</div>}
      </Consumer>
    );
  }
}

// 父组件
class App extends React.Component {
  render() {
    return (
      <Provider value="pink">
        <div className="app">
          <Node />
        </div>
      </Provider>
    );
  }
}

// 渲染父组件到页面
ReactDOM.render(<App />, document.getElementById('root'));
js
const child = props;
return (
  <div>
    <span>child > 3SSNaI6</span>
    <span>是子节点 -- {data}</span>
  </div>
);

4、 props深入

(1)children 属性

children 属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性

children 属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)

js
function Hello(props){
    return (<div> 组件的子节点:{props.children} </div>)
}
<Hel1o>我是子节点</He11o>

(2)props校验

对组件来说,props是外来的,无法保证组件使用者传入什么格式的数据

如果传入的数据格式不对,可能会导致组件内部报错

关键问题:组件的使用者不知道明确的错误原因

js
小明创建的组件 App

function App(props) {
  const arr = props.colors;
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>);
  return <ul>{lis}</ul>;
}

小红使用组件 App
<App colors={[{ name: '红' }, { name: '绿' }, { name: '蓝' }]} />

👉 props校验由来

从上面可以看出,如果传入的数据格式不对,可能会导致组件内部报错,项目开发过程往往多人协同开发,这个时候就需要一个机制来约束数据格式,让使用者知道如何正确使用组件,这就是props校验

👉 props校验介绍 允许在创建组件的时候,就指定props的类型、格式等

👉 props校验作用 捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

propTypes使用

👉认识 propTypes 是 React 中的一种用于类型检查的机制,用于验证组件的props是否符合预期类型,也就是我们认为的传入的参数类型是否正确。

👉 版本历史

JS
  React0.14版本:最初作为React的一部分引入的
  
  React15.5版本:Reac 团队将propTypes 移到一个独立的包中,并且开始鼓励开发者通过安装 prop-types 包来使用它

👉安装使用

🍆使用步骤

JS
1. 安装包 prop-types(yarn add prop-types/ npmi props-types)
2. 导入 prop-types包
3. 使用组件名.propTypes=来给组件的props添加校验规则

上面的部分其实就是我们propTypes的代码和写法:

JS
// 完整写法
import PropTypes from 'prop-types';

const App = ({ colors }) => {
  // 你的组件代码
  return (
    <div>
      {colors.map((color, index) => (
        <p key={index} style={{ color }}>
          {color}
        </p>
      ))}
    </div>
  );
};
App.propTypes = {
  colors: PropTypes.array.isRequired,  // 定义 colors 必须是数组类型并且是必需的
};
export default App;

👉错误提示

我们简单看一下下面的代码错误的时候给我们的错误提示:

JS
// 完整写法
import PropTypes from 'prop-types';

const App = ({ colors }) => {
  // 你的组件代码
  return (
    <div>
      {colors.map((color, index) => (
        <p key={index} style={{ color }}>
          {color}
        </p>
      ))}
    </div>
  );
};
App.propTypes = {
  colors: PropTypes.array.isRequired,  // 定义 colors 必须是数组类型并且是必需的
};
export default App;

错误写法时候给我们的提示

JS
import PropTypes from 'prop-types';
function App(props) {
  return (
    <h1>Hi, {props.colors}</h1>
  );
}

// 定义propTypes
App.propTypes = {
  // 约定colors属性为array类型
  // 如果类型不对,则报出明确错误,便于分析错误原因
  colors: PropTypes.array
};

export default App;

这个时候给我们的提示 image.png

👉 常见propTypes约束

  1. 常见类型:array、bool、func、number、object、string
  2. React元素类型:element
  3. 必填项:isRequired
  4. 特定结构的对象:shape(())

👉写法

JS
// 常见类型

// 可选函数
optionalFunc: PropTypes.func,

// 必选函数
requiredFunc: PropTypes.func.isRequired,

// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
  color: PropTypes.string,
  fontSize: PropTypes.number
})

👉完整类型

约束地址 image.png

PropType描述
PropTypes.string字符串类型
PropTypes.number数字类型
PropTypes.bool布尔类型
PropTypes.array数组类型
PropTypes.object对象类型
PropTypes.func函数类型
PropTypes.node任意可渲染的内容(比如:字符串、数字、React 元素、数组等)
PropTypes.elementReact 元素类型
PropTypes.instanceOf(Class)验证值是否为某个类的实例
PropTypes.oneOf([value1, value2, ...])验证值是否为指定的某一个值
PropTypes.oneOfType([type1, type2, ...])验证值是否匹配多个类型中的任意一个
PropTypes.arrayOf(type)验证数组中的每一项是否符合指定类型
PropTypes.objectOf(type)验证对象的每个值是否符合指定类型
PropTypes.shape({})验证对象的属性是否符合指定类型
PropTypes.any任意类型

👉案例

JS
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';

const App = (props) => {
  return (
    <div>
      <h1>props 校验:</h1>
    </div>
  );
};

// 添加 props 校验
App.propTypes = {
  a: PropTypes.number,  // 属性 a 的类型:数值 (number)
  fn: PropTypes.func.isRequired,  // 属性 fn 的类型:函数 (func) 并且为必填项
  tag: PropTypes.element,  // 属性 tag 的类型:React 元素 (element)
  filter: PropTypes.shape({  // 属性 filter 的类型:对象
    area: PropTypes.string,
    price: PropTypes.number,
  }),
};

ReactDOM.render(<App />, document.getElementById('root'));

👉默认值defaultProps

有时候我们需要一些默认值,当父组件没有传递 props 时,我们使用默认值defaultProps。

场景:分页组件每页显示条数 作用:给props设置默认值,在未传入props时生效

JS
import React from 'react';
import ReactDOM from 'react-dom';

function App(props) {
  return (
    <div>
      此处展示props的默认值:{props.pagesize}
    </div>
  );
}

// 设置默认值
App.defaultProps = {
  pagesize: 10,  // 默认值为 10
};
ReactDOM.render(<App />, document.getElementById('root'));

Released under the MIT License.