父状态更新时,将子级重新渲染

时间:2020-10-13 12:05:14

标签: reactjs typescript components

我当前正在创建一个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);

我的主要问题是,每当我单击“所有复选框”时,其他条目都不会更新... 在“ MultiCheckboxComponent”上更改了状态get,但在“ Checkbox”上未调用“构造函数”,因此其状态未更新并正确呈现。我是React的新手,我想创建一个没有“ redux-store”的组件,该组件可用于不同形式(本地存储),并将其值/状态向上填充到一个更特定的组件中,以将其存储在redux中。 >

赞:

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

但是,每当我在MultiCheckboxComponent上调用“ setState()”时,都会发生“渲染”,并且也发生在CheckboxComponent上,但是不会使用“ props”(未调用“ Constructor”)。如何在“父母”的“孩子”上设置状态?

1 个答案:

答案 0 :(得分:1)

我认为您遇到了麻烦,因为您正在考虑应如何以某种错误的方式对此类事物进行建模。

构造函数仅在创建组件时被调用一次。我认为复选框组件内部的状态不是必需的-毕竟-它们似乎在很大程度上复制了道具。

在编写React代码时-将您的props视为更改值,这些值将触发子组件中的更新。使用它们来影响组件的外观。

这是我要进行的一些更改:

这将为您提供以下内容:


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);

如果您需要任何进一步的帮助,请告诉我!