Nano Hash - криптовалюты, майнинг, программирование

Рендеринг дочернего элемента при обновлении родительского состояния

В настоящее время я создаю компонент MultiCheckbox, например:

Checkbox.tsx

import './Checkbox.scss';

import React, {ChangeEvent, Component,} from 'react';

/*
 * The properties of Checkbox
 */
interface ICheckboxProps<TYPE> {
    id: string;
    name: string;
    value: TYPE;
    checked?: boolean;
    //if a cross should be used instead of a hook
    cross?: boolean;
    disabled?: boolean;
    //should show a label on the right side of the checkbox
    showLabel?: boolean;
    //get the string for the value of the checkbox
    labelFunc?: (value: TYPE) => string;
    // an onChange function which gets called with the state.checked argument
    onChange?: (checkbox: ICheckboxState<TYPE>) => void;
}

interface ICheckboxState<TYPE> {
    // checked state of the checkbox
    checked: boolean;
    value: TYPE;
}

class CheckboxComponent<TYPE> extends Component<ICheckboxProps<TYPE>, ICheckboxState<TYPE>> {
    constructor(props: ICheckboxProps<TYPE>) {
        super(props);
        console.log(props);
        // set the initial state
        this.state = {
            checked: props.checked == null ? false : props.checked,
            value: props.value,
        };
        console.log(props);
    }

    /*
     * Render the component as ReactElement
     */
    public render(): JSX.Element {
        console.log('Checkbox render: ');
        console.log(this.state);
        return (
            <div className={'checkbox'}>
                <input
                    id={this.props.id}
                    type={'checkbox'}
                    name={this.props.name}
                    className={this.props.cross === true ? 'checkbox-cross' : 'checkbox-hook'}
                    onChange={this.onInputElementChangeEvent}
                    checked={this.state.checked}
                    disabled={this.props.disabled}
                />
                {this.props.showLabel === true ? (
                    <label
                        className="checkbox-label"
                        htmlFor={this.props.id}>
                        {typeof this.props.labelFunc === 'function' ?
                            this.props.labelFunc(this.state.value) : String(this.state.value)}
                    </label>
                ) : null}
            </div>
        );
    }

    private onInputElementChangeEvent = (e: ChangeEvent<HTMLInputElement>): void => {
        this.onChange(e.target.checked);
    }

    private onChange(checked: boolean): void {
        // set the new state when the "onChange" event of the checkbox happens
        this.setState({
            checked: checked,
            value: this.state.value,
        }, () => {
            // if there is an onChange function supscribed to the event handler than execute it with the current "checked" as
            if (typeof this.props.onChange === 'function') {
                this.props.onChange(this.state);
            }
        });
    }

    public isChecked(): boolean {
        return this.state.checked;
    }

    //return only the value if it's checked
    public getValue(): TYPE {
        return this.state.value;
    }
}

export const Checkbox = (CheckboxComponent);

и

MultiCheckbox.tsx

import './MultiCheckbox.scss';

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

/*
 * The properties of Checkbox
 */
interface IMultiCheckboxProps<TYPE> {
    id: string;
    values: TYPE[];
    idFunc: (value: TYPE) => any;
    //if a cross should be used instead of a hook
    cross?: boolean;
    initialChecked?: boolean;
    disabled?: boolean;
    //get the string for the value of the checkbox
    labelFunc: (value: TYPE) => string;
    // an onChange function which gets called with the state.checked argument
    onChange?: (selected: TYPE[]) => void;
    //all checkbox
    allButton?: boolean;
    //empty checkbox value
    emptyButton?: boolean;
    //label for empty checkbox
    emptyLabel?: string;
}

interface IMultiCheckboxState<TYPE> {
    values: SelectedValue<TYPE>[];
    all: boolean;
    empty: boolean;
}

interface SelectedValue<TYPE> {
    id: any;
    value: TYPE;
    selected: boolean;
}

class MultiCheckboxComponent<TYPE> extends Component<IMultiCheckboxProps<TYPE>, IMultiCheckboxState<TYPE>> {
    constructor(props: IMultiCheckboxProps<TYPE>) {
        super(props);

        // set the initial state
        this.state = {
            values: props.values.map(value => {
                return {
                    id: props.idFunc(value),
                    value: value,
                    selected: props.initialChecked == null ? false : this.props.initialChecked
                };
            }),
            all: props.initialChecked == null ? false : this.props.initialChecked,
            empty: false
        };
    }

    /*
       * Render the component as ReactElement
       */
    public render(): JSX.Element {
        console.log('render')
        console.log(this.state);
        const id = 'multicheckbox-' + this.props.id;
        const subId = id + '-checkbox-';
        var checkboxes = this.state.values.map(value =>
            <Checkbox
                key={subId + value.id}
                id={subId + value.id}
                name={this.props.labelFunc(value.value)}
                checked={value.selected}
                showLabel={true}
                value={value.value}
                labelFunc={this.props.labelFunc}
                cross={this.props.cross}
                disabled={this.props.disabled}
                onChange={(state) => this.onCheckboxChanged(state.checked, state.value)}
            />
        );

        if (this.props.allButton) {
            checkboxes = checkboxes.concat(
                <Checkbox
                    key={subId + 'all'}
                    id={subId + 'all'}
                    name={'Alle'}
                    value={'Alle'}
                    showLabel={true}
                    labelFunc={(value) => value}
                    cross={this.props.cross}
                    disabled={this.props.disabled}
                    checked={this.state.all}
                    onChange={(state) =>
                        this.setAllChecked(state.checked)
                    }
                />
            );
        }

        if (this.props.emptyButton) {

        }

        console.log(checkboxes);

        return (
            <div
                id={id}
                key={id}
            >{checkboxes}</div>
        );
    }

    private onCheckboxChanged(checked: boolean, value: TYPE): void {
        alert(value.toString() + ' is checked: ' + checked);
        //TODO set boolean true/false on this.state.values -> checked!
    }

    private setAllChecked(checked: boolean): void {
        console.log(checked);
        console.log(this.state);
        this.setState({
            values: this.state.values.map(val => {
                return {
                    id: val.id,
                    value: val.value,
                    selected: checked
                };
            }),
            all: checked,
            empty: this.state.empty
        }, this.onSelectedChanged);
    }

    private onSelectedChanged(): void {
        if (this.props.onChange) {
            this.props.onChange(this.state.values.map(value => {
                return value.value
            }));
        }
    }

}

export const MultiCheckbox = (MultiCheckboxComponent);

И моя главная проблема заключается в том, что всякий раз, когда я нажимаю All-Checkbox, другие записи не обновляются... Состояние изменяется в MultiCheckboxComponent, но конструктор не вызывается в Checkbox, поэтому его состояние не обновляется и не отображается правильно . Я новичок в React и хочу создать компонент без избыточного хранилища, который можно использовать в разных формах (локальное хранилище) и заполнять его значения/состояния вверх до более конкретного компонента, который хранит его в избыточном.

Нравиться:

  1. FooComponent (list of Foo) -> MultiCheckboxComponent -> multiple Checkboxes
  2. FeeComponent (list of Fee) -> MultiCheckboxComponent -> multiple Checkboxes
  3. LuuComponent (stuff) -> single Checkbox

Но всякий раз, когда я вызываю setState() для MultiCheckboxComponent, происходит рендеринг, и рендеринг также происходит для CheckboxComponent, но реквизиты не используются (конструктор не вызывается). Как я могу установить состояние для ребенка от родителя?


Ответы:


1

Я считаю, что у вас проблемы, потому что вы думаете о том, как такие вещи должны быть смоделированы в реакции, немного неправильным образом.

Конструктор вызывается только один раз — при создании компонента. Я не думаю, что состояние внутри ваших компонентов флажка необходимо - в конце концов - они, похоже, в значительной степени дублируют реквизит.

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

Вот некоторые изменения, которые я бы сделал:

  • Избавьтесь от состояния флажка - возможно, оно вам не нужно
  • Поскольку вам не нужно состояние, вы можете написать чистый функциональный компонент (без сохранения состояния). Это делает ваш код более понятным, так как в нем меньше движущихся частей.
  • Разрушьте свой реквизит, чтобы не повторять this.props.X
  • Используйте короткое замыкание, чтобы уменьшить многословность (возврат ложного значения средству визуализации игнорирует его — подробнее об этом здесь https://reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--оператор)

Это оставляет вам следующее:


interface ICheckboxProps<TYPE> {
  id: string;
  name: string;
  value: TYPE;
  checked?: boolean;
  //if a cross should be used instead of a hook
  cross?: boolean;
  disabled?: boolean;
  //should show a label on the right side of the checkbox
  showLabel?: boolean;
  //get the string for the value of the checkbox
  labelFunc?: (value: TYPE) => string;
  // an onChange function which gets called with the state.checked argument
  onChange?: (checkbox: ICheckboxState<TYPE>) => void;
}

function CheckboxComponent<TYPE>(props: ICheckboxProps<TYPE>) {
  console.log('Checkbox render: ');
  // Destructure props - save writing out this.props.foo each time
  const { name, id, cross, onChange, checked, disabled, showLabel } = this.props;

  return (
    <div className="checkbox">
      <input
        id={id}
        type="checkbox"
        name={name}
        className={cross ? 'checkbox-cross' : 'checkbox-hook'}
        onChange={(e) => onChange && onChange(e.target.checked)}
        checked={checked}
        disabled={disabled}
      />
      {showLabel && (
        <label className="checkbox-label" htmlFor={id}>
          {labelFunc ? labelFunc(value) : value}?
        </label>
      )}
    </div>
  );
}

export const Checkbox = (CheckboxComponent);

Дайте мне знать, если вам нужна дополнительная помощь с этим!

13.10.2020
  • Но, делая это так, я больше не могу использовать флажок как неконтролируемый компонент. 13.10.2020
  • Я не уверен, что вы имеете в виду - в настоящее время это не неконтролируемый компонент. Иметь компонент как неконтролируемый означает позволить DOM быть единственным источником правды, из которого вы затем извлекаете значения в тот момент, когда вам это нужно (т.е. после того, как вы закончите с формой). На данный момент ваше состояние (в некоторой степени) контролируется родителем - по крайней мере, оно хранится в родительском элементе, но ссылка для обновления значения в дочернем элементе не работает. 13.10.2020
  • Подробнее о неконтролируемых компонентах см. здесь. Вы используете ссылку для извлечения значения из DOM reactjs.org/docs/uncontroller-components.html. 13.10.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..