Общие знания о взаимодействии компонентов React

React.js

предисловие

Эта статья является технической статьей начального уровня и читает заметки, пожалуйста, игнорируйте ее. демонстрационный адрес:Связь

Взаимодействие родительского и дочернего компонентов (данные)

Связь между родительскими компонентами и дочерними компонентами очень распространена в нашей разработке, а метод вообще является самым прямым и удобным атрибутом props.Конечно, это относится к отношениям между родительскими и дочерними компонентами напрямую.

Здесь я просто хочу напомнить всем, что если вам это нужно, не изменяйте напрямую значение входящего свойства.Если вы попытаетесь изменить его напрямую, будет подсказка, что этого делать не следует.Эта переменная читается- Только. Вы можете принять и изменить состояние, если вам действительно нужно.

Ребенок общается с родительским компонентом

Существует два основных способа связи, а именно настраиваемые события и функции обратного вызова. Среди них функция обратного вызова очень удобна и может получить весь статус во время выполнения; среди них пользовательское событие более хлопотно, и мы обычно используем его меньше.

В приведенном ниже примере я использую функцию обратного вызова.

// listItem.jsx
 render() {
    const { title } = this.state;
    const { click } = this.props;
    const title2 = title + "temp";
    return (
      <h2 onClick={click} onMouseDown={this.down}>
        {title2}
      </h2>
    );
  }

// list.jsx
render() {
    const { list } = this.state;
    const style = {
      height: 30
    };
    return (
      <div style={style}>
        {list.map(item => (
          <ListItem
            {...item}
            click={this.click.bind(this)}
            error={this.error}
          />
        ))}
      </div>
    );
  }

Примечание: Если вы хотите передать событие как внешний параметр, вам нужно привязать параметр или написать его как простую стрелочную функцию. В противном случае непосредственно записываемые функции и параметры будут компилироваться в функции и выполняться напрямую. (Например, передайте текст onClick = {text => this.click(text) или напишите onClick={this.click.bind(this,text)}).

Информационный бюллетень о компонентах Brother

Если родственные компоненты являются одним и тем же родительским компонентом, они могут взаимодействовать с родительским компонентом. Я не буду описывать это здесь. Здесь я делюсь реализацией механизма публикации и подписки на события. Мы используем модуль Events в nodejs.

Создайте одноэлементное событие в корневом каталоге и экспортируйте его.Чтобы избежать потерь, мы используем одноэлементный файл events.js глобально.

import {EventEmitter} from 'events';
export default new EventEmitter();
// a组件中触发
import emitter from '../events';
emitter.emit('clickMsg','点击了')

// b组件监听
commpontentDidMount(){
	this.clickMsg = emitter.on('clickMsg',data => {
  	console.log(data)
  })
}
compontWillUnmount(){
	emitter.removeListener(this.clickMsg)
}

Примечания. Будет ли выполнение события выполняться несколько раз? В настоящее время возникла проблема, журнал будет выполняться несколько раз, описание в файле, перехват конструктора консоли и пересылка сообщений на обратный вызов 。

Межуровневая связь между компонентами -- контекст

Мы также можем думать о кросс-уровневых компонентах как о суперпозиции родительско-дочерних компонентов, поэтому мы можем использовать передачу данных многоуровневых родительско-дочерних компонентов, но это громоздко, поэтому более рекомендуемый способ — реализовать его через контекст. . Его принцип также относительно прост, исходя из предпосылки того же родительского компонента, его дочерние компоненты на любом уровне могут получить статус родительского приложения и общаться с ним.

В предыдущей версии официального сайта эта функция всегда была доступна, но использовать ее не рекомендуется, но с итерацией версии я постепенно обнаружил, что эта функция имеет большое удобство.

// 定义一个context的全局对象:需要注意的是你在provider的地方使用它的时候也必须符合基本的数据结构
import React from "react";
export const ThemeContext React.createContext({
  color: "black",
  label: "名字"
});
// 父或者顶层容器设计
import { ThemeContext} from "../themeContext";
	constructor(props){
    super(props);
    this.state = {
    	theme:{
        color:'pink',
        label:'你的名字'
      }
    }
  }
render(){
  return (
<ThemeContext.Provider value={this.state.theme}>
 </ThemeContext.Provider>
  )
}
// 任意层级的子组件
import {ThemeContext} from "../../themeContext";

render(){
			<ThemeContext.Consumer>
        {({ color, label }) => <label style={{ color: color }}>{label}</label>}
      </ThemeContext.Consumer>
} 
  • Динамический контекст: мы можем динамически предоставлять или изменять его значение по умолчанию непосредственно в провайдере.
  • Рекомендации по использованию: Не используйте контекст вслепую.Рекомендуется вводить контекст для решения этой проблемы только тогда, когда несколько компонентов используют одни и те же общие данные.
  • Возможности: Предоставляет глобальный объект контекста или объект контекста, доступный любому компоненту, затем компоненты реализуются путем изменения или использования этого объекта контекста.

Контекст против redux.

  • Первоначальный метод передачи — каскадная передача, а новый метод — прямая передача, который более эффективен.

image.png
 

  • Исходный метод каскадный.Если определенный и willUpdate перехватывает и возвращает false, то его потомки не могут быть нормально получены.В новой версии этой проблемы нет.

Использование в компонентах формы ant-design

В компоненте формы antd мы также видим кодовую часть соответствующего контекста: адрес файла:Связь

// context 设置的默认值 
import createReactContext, { Context } from 'create-react-context';
import { ColProps } from '../grid/col';

export interface FormContextProps {
  vertical: boolean;
  colon?: boolean;
  labelAlign?: string;
  labelCol?: ColProps;
  wrapperCol?: ColProps;
}

export const FormContext: Context<FormContextProps> = createReactContext({
  labelAlign: 'right',
  vertical: false,
});
// 引入默认的context
import { FormContext } from './context';
// 获取传入的属性,然后解构,通过context provider的方式,state传值提供给包含在form组件中的组件  
render() {
    const { wrapperCol, labelAlign, labelCol, layout, colon } = this.props;
    return (
      <FormContext.Provider
        value={{ wrapperCol, labelAlign, labelCol, vertical: layout === 'vertical', colon }}
      >
        <ConfigConsumer>{this.renderForm}</ConfigConsumer>
      </FormContext.Provider>
    );
  }

Родительский компонент вызывает событие дочернего компонента

Хотя в приведенных выше методах упоминаются различные механизмы связи, до сих пор не существует соответствующего метода для события, которое родительскому компоненту необходимо для активного запуска дочернего компонента. Мы даем сценарий для этого требования в примере ниже. Например: в компоненте-контейнере нам нужно активно обновлять данные в списке, а данные компонента-списка получаются под нашим собственным контролем.

Примечания: Конечно, некоторые люди скажут, что раз есть такой спрос, почему бы не передать данные или методы дочернего компонента через свойства родительского компонента и сохранить логику в родительском компоненте, чтобы не было нет такой проблемы. Хотя это очень хороший метод, бывают случаи, когда мы хотим, чтобы дочерний компонент имел больше функций, вместо того, чтобы сильно полагаться на родительский компонент, а только раскрывать свои собственные события родительскому компоненту. На самом деле такая дизайнерская идея есть во Vue, а в UI-фреймворке с открытым исходным кодом их много, которые называются кастомными событиями.Помимо передачи атрибутов в компоненты управления, мы также можем реализовать механизм изменения компонентов через кастомные события и вызовы событий компонентов.

Метод 1: метод ссылок

class List extends React.Component{
  constructor(props){
    this.state ={
    desc:''
    }
  }
  xxxMethod(){
    const desc = Math.random();
  	this.setState({
    desc
    })
  }
 render(){
   const {list} = this.props;
 	return (
  <div>
      <button onClick={this.xxxMethod.bind(this)}>点击变动标题描述</button>
      {list.map(text =>(<h2>{text}{desc}</h2>))}
  </div>    
  )
 }
}

class parent extends React.Component{
refresh(){
 // 可以调用子组件的任意方法
this.refs.List.xxxMethod();
}
//父组件
render (){
  return (
    <div>
        <button onClick={this.refresh.bind(this)}>点击变动标题描述</button>
        <List ref="List" />
    </div>)
} 
}
    

Метод 2 Верните это через активный вызов

Это передается обратно через событие обратного вызова, а затем назначается родительскому компоненту. Кажется, что нет никакой разницы между этим абзацем и прямым использованием ref, но несомненно, что таким образом мы можем точно получить этот экземпляр дочернего компонента. Почему оно разумно существует? Потому что в некоторых случаях то, что мы получаем через ref, не является свойством или методом в дочернем компоненте, который нам нужен.

class Child extends React.Component{
	constructor(props){
  	super(props);
  }
  componentDidMount(){
  	this.props.onRef(this)
  }
  console(){
  	console.log('子组件的方法');
  }
  render(){
  	return (<div>子组件</div>)
  }
}

class Parent extends React.Component{
  onRef(comp){
  	this.child = comp;
  }
  console(){
  	this.child.console()
  }
 render(){
  return (
  <div>
      <button onClick={this.console.bind(this)}>点击执行子组件方法</button>
      <Child onRef={this.onRef.bind(this)}/>
 </div>    
  )
 }
}

Метод 3: НОЦ

С помощью компонентов более высокого порядка мы можем решить схожие требования одновременно, вместо того, чтобы каждый раз вручную писать этот метод воздействия. Введите его там, где вам это нужно, а затем напишите @withRef в родительском и дочернем компонентах соответственно.

import React from "react";
export default WrappedComponent => {
  return class withRef extends React.Component {
    static displayName = `withRef(${WrappedComponent.displayName ||
      WrappedComponent.name ||
      "Component"})`;
    render() {
      // 这里重新定义一个props的原因是:
      // 你直接去修改this.props.ref在react开发模式下会报错,不允许你去修改
      const props = {
        ...this.props
      };
      // 在这里把getInstance赋值给ref,
      // 传给`WrappedComponent`,这样就getInstance能获取到`WrappedComponent`实例
      // 感谢评论区的[yangshenghaha]同学的完善
      props.ref = el => {
        this.props.getInstance && this.props.getInstance(el);
        this.props.ref && this.props.ref(el);
      };
      return <WrappedComponent {...props} />;
    }
  };
};

Если ваш синтаксис неверен и декораторы не поддерживаются, вы можете использовать следующие настройки: установить зависимости пакета, npm установить @babel/plugin-proposal-decorators. Затем вам нужно установить конфигурацию плагина в package.json:

 "babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ],
    "presets": [
      "react-app"
    ]
  },

резюме

В этом разделе мы познакомились с тем, как общаться между базовыми родительско-дочерними компонентами React, родственными компонентами и межуровневыми компонентами.Дополнительный момент, который нам нужно знать, это то, что общение относится не только к обмену данными, но и к обмену событиями. , например active В компоненте мы можем легко вызвать метод родительского компонента или данные, переданные через свойство, и нам также нужно знать, как выставить событие дочернего компонента родительскому компоненту для использования.

На самом деле, основное внимание будет уделено следующей статье, абстракции компонентов, конечно, высокоуровневым компонентам, но на этот раз я черпал все больше и больше вдохновения из «Углубленного стека технологий React». Нажмите, чтобы прыгнуть:Абстракция компонента React