我正在与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});
}
}