当子组件更改redux状态时,React父组件将重新呈现

时间:2016-11-09 18:43:05

标签: javascript reactjs redux react-redux

我正在与React合作创建一个表单来编辑我存储在Redux Store中的数据。数据包含简单的键值对,其中值为字符串。它还包含键值对,其中值是一个字符串数组。

我创建了一个手风琴组件,允许我渲染一个输入列表,每个项目一个。此列表可以打开和关闭。此项还允许向正在编辑的数组添加更多项目。

我遇到的问题是,当更新项目数组时,重新呈现整个列表会导致用户无法专注于他们输入的输入。

我已尝试在componentShouldUpdate()中进行一些检查,但感觉非常hackey并导致其他问题并且大大增加了复杂性。

我的理解是React将确定需要重新渲染的内容并仅重新渲染该单个组件,在这种情况下,该组件将是输入的输入字段。

非常感谢任何帮助,在此先感谢。你可以在下面找到我的代码。

SimpleInputList.js

import React, { Component } from 'react';
import SimpleInput from './SimpleInput';
import { TextInput } from 'components';
import { connect } from 'react-redux';
import {
    fieldChangeAtIndex,
    fieldPush
} from '../../redux/modules/reducer';
import uuid from 'node-uuid';

import classnames from 'classnames/bind';
import css from '../../css/inputs.css';
const cx = classnames.bind(css);

@connect(state => ({
    product: state.data.product
    }), {
        fieldChangeAtIndex,
        fieldPush
    }
)
export default class SimpleInputList extends Component {

    // Hackey state stuff
    state = {
        isOpen: false,
        prevItemLength: 0
    }

    get items() {
        const { fieldName, product } = this.props;
        return product[fieldName];
    }

    get innerClasses() {
        const classes = ['inner'];
        return this.state.isOpen
            ? [...classes, 'open']
            : classes;
    }

    // Hackey state stuff
shouldComponentUpdate = (nextProps, nextState) => {

    const { fieldName, product } = this.props;
    const newItems = nextProps.product[nextProps.fieldName];
    const itemsDiff = Math.abs(this.state.prevItemLength - newItems.length);
    const isOpenChanged = this.state.isOpen !== nextState.isOpen;

    return isOpenChanged || itemsDiff;
}
 // Hackey state stuff
componentDidMount() {
    this.setState({isOpen: false});
}
 // Hackey state stuff
componentDidUpdate() {
    this.setState({prevItemLength: this.items.length});
}
    toggleHeader = () => {
        this.state.isOpen
            ? this.setState({ isOpen: false })
            : this.setState({ isOpen: true });
    }

    pushNew = () => {

        const {fieldPush, fieldName, label} = this.props;

        fieldPush({
            key: fieldName,
            value: `New ${label}`
        });
    }

    render() {

        const {
            label,
            fieldName,
            product,
            fieldChangeAtIndex,
            beforeDispatch,
            beforeRender
        } = this.props;

        const changeList = ListOnChange(fieldName, this.items, fieldChangeAtIndex, beforeDispatch);

        return (
           <div>
                <div className={cx('wrapper')} onClick={this.toggleHeader}>
                    <label className={cx('label')}>{label}</label>
                </div>

                <div className={cx(...this.innerClasses)}>
                    { this.items.map((item, i) => {

                        const itemVal = beforeRender
                            ? beforeRender(item)
                            : item;

                        return
                        <SimpleInput
                            key={uuid.v4()}
                            value={itemVal}
                            fieldName='_'
                            onChange={changeList(i)}
                            useDefaultValue
                        />

                    }) }
                    <a href="#ADD_{label}"
                        className="Add"
                        onClick={this.pushNew} >Add {label}</a>
                </div>

            </div>
        );
    }
}

function ListOnChange (key, items, onChangeFunc, beforeDispatch) {
    return index => value => {
        let valueToSave = value._;
        if (beforeDispatch) {
            valueToSave = beforeDispatch(valueToSave, index);
        }
        onChangeFunc({key, value: valueToSave, index});
    }
}


SimpleInput.js

import React, { Component } from 'react';
import { TextInput } from 'components';
import { connect } from 'react-redux';
import {
    fieldChangeAtIndex,
    fieldPush
} from '../../redux/modules/reducer';
import uuid from 'node-uuid';

import classnames from 'classnames/bind';
import css from '../../css/inputs.css';
const cx = classnames.bind(css);

export default class SimpleInput extends Component {
    render() {

        const {
            label,
            fieldName,
            value,
            useDefaultValue,
            inList,
            onChange,
            beforeDispatch
        } = this.props;

        const { setFocus, unsetFocus } = this;

        const setValue = {
            [useDefaultValue ? 'defaultValue' : 'value']: value
        };

        return (
           <div className={cx('wrapper')}>
                <label>
                { label
                    ? <span className={cx('label')}>{label}</span>
                    : null
                }
                    <TextInput
                        {...setValue}
                        onChange={simpleOnChange(fieldName, onChange, beforeDispatch)}
                    />
                </label>
           </div>
        );
    }
}

function simpleOnChange (key, onChangeFunc, beforeDispatch) {
    return val => {
        const valToSave = beforeDispatch
            ? beforeDispatch(val)
            : val;

        onChangeFunc({[key]: valToSave});
    }
}

0 个答案:

没有答案