Самый простой способ, используя функцию стрелки:
class InputsList extends React.Component {
render() {
const items = this.props.fields.map((field, i) => (
<YourInputComponent onChange={
(e) => this.props.onChange(i, e.target.value) } />
);
return (<div>{ items }</div>);
}
}
Проблема этого подхода заключается в том, что функция стрелки внутри onChange
будет перераспределять новую функцию каждый раз, когда вызывается метод render.Это может вызвать проблемы с производительностью из-за:
- Чистые компоненты будут рендериться снова, так как старая функция (стрелка) и новая функция (стрелка) будут отличаться, даже если оба будут вызывать одну и ту же функцию.
- Затраты на сборщик мусора для избавления от этих старых функций.
Лучшим подходом будет передача любых соответствующих данных до YourInputComponent
, который будет отвечать за вызовonChange
с соответствующим индексом / идентификатором, который позволяет вам определить, какой вход вызвал событие.
class InputsList extends React.Component {
render() {
const items = this.props.fields.map((field, i) => (
<YourInputComponent index={ i } onChange={ this.props.onChange } />
);
return (<div>{ items }</div>);
}
}
В YourInputComponent
у вас будет что-то вроде этого:
this.props.onChange(this.props.index, e.target.value);
Сейчасфункция, которую вы передаете onChange
, всегда будет одинаковой, если только она не изменится в родительском компоненте, поэтому YourInputComponent
не будет перерисована.
Здесь у вас есть рабочий примерпервого метода:
class List extends React.PureComponent {
render() {
const { values, length } = this.props;
const items = [];
for (let i = 0; i < length; ++i) {
const value = values[i];
items.push(<li key={ `${ value }-${ i }` } className="list__item">{ value }</li>);
}
return (<ul>{ items }</ul>);
}
}
class InputsList extends React.PureComponent {
render() {
const { name, label, totalInputs, onChange } = this.props;
const inputs = [];
for (let i = 0; i < totalInputs; ++i) {
inputs.push(
<li key={ i }>
<label className="inputs__label">
{ `${ label } ${ i + 1 }` }
<input
className="inputs__input"
type='text'
name={ `${ name }-${ i }` }
onInput={ (e) => onChange(i, e.target.value) } />
</label>
</li>
);
}
return (<ul>{ inputs }</ul>);
}
}
class RadioTabs extends React.PureComponent {
render() {
const { name, value, options, onChange } = this.props;
const radios = options.map(option => (
<li key={ option }>
<label className="radioTabs__label">
<input
className="radioTabs__input"
type="radio"
value={ option }
checked={ value === option }
name={ name }
onChange={ onChange } />
<span className="radioTabs__text">{ option }</span>
</label>
</li>
));
return(
<ul className="radioTabs__base">
{ radios }
</ul>
);
}
}
class App extends React.Component {
static options = [1, 2, 3, 4, 5, 6, 7];
constructor(props) {
super(props);
this.state = {
totalInputs: App.options[0],
values: [],
};
}
onTotalInputsChange = (e) => {
this.setState({
totalInputs: parseInt(e.target.value),
values: this.state.values,
});
};
onInputsChange = (index, value) => {
const values = [ ...this.state.values ];
values[index] = value;
this.setState({
totalInputs: this.state.totalInputs,
values,
});
};
render() {
const { totalInputs, values } = this.state;
return(
<div className="app">
<div className="header">
<RadioTabs
name="days"
value={ totalInputs }
options={ App.options }
onChange={ this.onTotalInputsChange } />
</div>
<div className="columns">
<div className="left">
<InputsList
name="values"
totalInputs={ totalInputs }
label="Day"
onChange={ this.onInputsChange } />
</div>
<div className="right">
<List values={ values } length={ totalInputs }/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
body {
font-family: monospace;
margin: 0;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
.app {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
padding: 4px;
border-bottom: 2px solid black;
flex: 0 0 auto;
}
.columns {
display: flex;
flex: 1 1 auto;
}
.left,
.right {
flex: 1 0 0;
overflow-y: scroll;
padding: 4px;
}
.left {
border-right: 2px solid black;
}
/* RADIO TABS */
.radioTabs__base {
display: flex;
align-items: center;
}
.radioTabs__label {
display: block;
padding: 4px 0;
cursor: pointer;
border-radius: 2px;
min-height: 27px;
min-width: 35px;
display: flex;
align-items: center;
justify-content: center;
}
.radioTabs__label:hover {
background: #EEE;
}
.radioTabs__input {
display: none;
}
.radioTabs__text {
display: block;
padding: 2px 4px 0;
border-bottom: 2px solid transparent;
}
.radioTabs__input:checked + .radioTabs__text {
border-bottom-color: black;
}
/* INPUTS LIST */
.inputs__label {
display: block;
padding: 4px 8px;
cursor: pointer;
border-radius: 2px;
display: flex;
align-items: center;
}
.inputs__label:hover {
background: #EEE;
}
.inputs__input {
border: 2px solid black;
padding: 4px 8px;
margin: 0 0 0 8px;
font-family: monospace;
flex: 1 0 auto;
}
/* LIST */
.list__item {
border-bottom: 2px solid black;
line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>