как передать реквизит многоуровневым компонентам в реакции?

avatar
Hongli Bu
9 августа 2021 в 02:21
119
2
1

Привет, я недавно работаю над реакцией, и у меня есть вопрос: если мы хотим передать свойства многоуровневым компонентам, как сделать это эффективно??

Скажем, у меня есть три компонента: родительский, дочерний, дочерний. Я хочу передать реквизит от Parent до ChildOfChild.

Я использовал componentWillReceiveProps в Child и ChildOfChild.

вот так: Родитель:

import React, {Component} from "react";
// @ts-ignore
import Child from "./Child";


class Parent extends Component<any, any> {

    constructor(props) {
        super(props);
    }
    render() {

        return (
            <Child filters={this.state.filters} />

        );
    }
}
export default () => {

    return (<Parent/>)
}

Дочерний элемент:


import React, {Component} from "react";
import ChildOfChild from "./ChildOfChild";

class Child extends Component<any, any> {
    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.filters !== this.props.filters) {
            this.setState({filters: nextProps.filters});
        }
    }


    public render() {
        return <ChildOfChild
            filters = {this.state.filters}
            uri = {this.state.uri}
            label = {this.state.label} />
    }
}

export default Child

и ChildOfChild:

import React from "react";
import {Component} from "react";

import * as _ from 'lodash';

import {makeStyles, createStyles} from '@material-ui/core/styles';
import {BackstageTheme} from '@backstage/theme'


const useStyles = makeStyles<BackstageTheme>(() =>
    createStyles({
        someStyle: {
            position: 'relative',
        }
    }),
);

interface Props {
    filters?: any,
    label?: string,
    uri?: string
}

class ChildOfChild extends Component<any, any> {

    constructor(props) {
        super(props);

    }


    componentWillReceiveProps(nextProps) {
        if (nextProps.filters !== this.props.filters) {
            this.fetchCountData(nextProps.filters);
        }

    }

    public componentDidMount() {

        this.fetchCountData(null);
    }

    public fetchCountData(filters) {
        if (filters != null && Object.keys(filters).length > 0) {
            // get data from api

        }
    }


    public render() {
        const popOverText = this.props.label;  // used somewhere
        const classes = this.props.classes;

        return (
            <p className={classes.someStyle}>
            {title}
            </p>
                
        );
    }
}

export default (props:Props) => {
    const classes = useStyles();
    return (
        <ChildOfChild classes={classes} {...props}/>
    )
}

Также я попробовал "componentDidUpdate", очень похожее использование, но, как упоминалось здесь: https://coderhelper.com/a/51313510/11650363, они разные.

Итак, я просто хочу спросить, каково мое решение? есть недостатки?? любые другие хорошие решения для передачи реквизита в несколько слоев??

Источник
Drew Reese
9 августа 2021 в 02:24
2

Во всех смыслах и целях componentWillReceiveProps устарело (reactjs.org/docs/…). Просто передайте реквизиты дочернему элементу, не нужно хранить в локальном состоянии среднего компонента, и фактически сохранение переданных реквизитов в состоянии является анти-шаблоном в React.

Hongli Bu
9 августа 2021 в 02:39
0

@DrewReese поможет вам немного уточнить, как передать реквизит, не сохраняя его в среднем компоненте, вы имеете в виду удалить средний компонент ???

Ответы (2)

avatar
Drew Reese
9 августа 2021 в 02:48
2

Сохранение переданных реквизитов в состоянии локального компонента является антипаттерном в React, вы должны передавать их дочерним элементам. componentWillReceiveProps во всех смыслах устарел и не должен использоваться. Для этого вы должны реализовать метод жизненного цикла componentDidUpdate и сверить предыдущие реквизиты/состояние с текущим значением состояния/реквизитов, чтобы вызвать любые побочные эффекты.

Родительский

class Parent extends Component<any, any> {
  state = {
    filters: [],
  }

  render() {
    return (
      <Child filters={this.state.filters} />
    );
  }
}

export default Parent

Ребенок

class Child extends Component<any, any> {
  constructor(props) {
    super(props);
    this.state = { ..... }
  }

  public render() {
    return (
      <ChildOfChild
        filters={this.props.filters} // <-- pass from props
        uri={this.state.uri}
        label={this.state.label}
      />
    );
  }
}

export default Child;

Ребенок Ребенок

class ChildOfChild extends Component<any, any> {
  constructor(props) {
    super(props);
    ....
  }

  public componentDidMount() {
    this.fetchCountData(this.props.filters);
  }

  public componentDidUpdate(prevProps) {
    if (propProps.filters !== this.props.filters) { // <-- check if props updated
      this.fetchCountData(this.props.filters);
    }
  }

  public fetchCountData(filters) {
    if (filters != null && Object.keys(filters).length > 0) {
      // get data from api
    }
  }  

  public render() {
    const popOverText = this.props.label;
    const classes = this.props.classes;

    return (
      <p className={classes.someStyle}>
        {title}
      </p>      
    );
  }
}
Hongli Bu
9 августа 2021 в 03:08
0

Отличное решение! Я просто запутался, почему не нужно componentDidUpdate в Child? Я помню, что изменение реквизита не приведет к повторному рендерингу компонента, верно?

Drew Reese
9 августа 2021 в 03:11
1

Компоненты @HongliBu React перерисовываются при обновлении их состояния и/или реквизита, или когда перерисовывается компонент-предок (т. е. он перерисовывает свое поддерево).

avatar
Jabal Logian
9 августа 2021 в 02:55
1

Я предлагаю вам использовать Контекст React (если вы знакомы с functional component в React). React Context — это библиотека из React для управления глобальными состояниями в вашем приложении. states могут быть переменными или функциями.

Вы также можете использовать другое глобальное управление состоянием, например React Redux (в вашем приложении не обязательно использовать functional component).

Представьте, что у вас есть приложение с такой иерархией.

enter image description here

Например, если вы хотите получить доступ к состоянию из компонента App в компоненте Table Cell, это истощит вашу энергию и время, если вы вручную передадите реквизиты от родительского компонента к дочернему.

С помощью React Context или React Redux вы можете сделать это с помощью нескольких строк кода.

Примечание: источник изображения.