1

I have inputs in my page added based on a period of time for example if the period is a week I would have 7 text inputs if the period is two weeks I would have 15 text inputs I already done that it is not the problem now i want to get the values entered in the created text fields knowing that in my render there is only one input that is duplicated a number of times according to the period given

this is how i render my inputs

render() {
    const days = [];
    let day = this.props.StartOfWeek;
    while (day <= this.props.EndOfWeek) {
      days.push(moment(day).format('ddd, DD-MM'));
      day = day.clone().add(1, 'd');
    }
    const daysCode = days.map((displayedDay) => {
    return (
        <td className={this.isWeek(displayedDay)}>
          <tr>{displayedDay}</tr>
          <tr>  <input type="text" key={displayedDay.id} size="3" /></tr>
        </td>);
    });

any ideas on how can i proceed ?

2 Answers 2

1

Easiest way, using an arrow function:

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

The problem with this approach is that the arrow function inside onChange will reallocate a new function every time render is called. That could cause performance issues due to:

  • Pure Components will render again, as the old (arrow) function and the new (arrow) function will be different, even thought both are calling the exact same fuction.
  • Garbage collector overhead to get rid of those old functions.

A better approach would be to pass any relevant data down to YourInputComponent, which will be responsible for calling the onChange with the relevant index/id that allows you to identify which input triggered the event.

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

In YourInputComponent you will have something like this:

this.props.onChange(this.props.index, e.target.value);

Now the function that you pass to onChange will always be the same, as long as it doesn't change in the parent component, so YourInputComponent won't be rerendered.

Here you have a working example of the first method:

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>

Sign up to request clarification or add additional context in comments.

2 Comments

in my exemple i don't have to trigger an event to add text inputs i did some updates on the post sorry it wasn't clear earlier
The idea is still the same, you will add a handler on that input, something like this (assumming you need a way to know which input triggered the event): <input type="text" key={ displayedDay.id } size="3" onInput={ (e) => this.onChange(displayedDay, e.target.value) } /> and you would implement onChange on that same class. displayedDay would allow you to identify the input.
0

i find the solution it was so silly, using jquery

  $('.App input').each((index, input) => {
  alert(input.value);

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.