Andrew Lovato
Andrew Lovato

Reputation: 133

Multiple Inputs in a react component, saving each index

I am generating a random number of inputs which will each collect a word. I want to then take all of those individual words and create an array of words (eventually to display that array). I am new to react and am using onChange for my inputs but all I get is a blob of all the words together. I don't know how to take the index of each of the inputs and put it into an array on submit. I don't know if I should use onSubmit or onChange. I guess I am not sure how to collect the information from each input on submit. do I use key={index} along with the event.target.value, do I just make an array of event.target.value?

Here is the Code Sandbox:

https://codesandbox.io/s/jovial-euler-swfs7?file=/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Image } from './Image.js'
import { Button } from './Button.js'
import { images } from './assets/images.js'
import { Countdown } from './Countdown.js'
import { DisplayCount } from './DisplayCount.js'
import { Inputs } from './Inputs.js'

class Game extends React.Component{
  constructor(props){
    super(props)

    this.timer = null


    this.state = {
      currentImg: 0,
      timer: null,
      ranNum: null,
      thought: null
    }

    this.handleClick = this.handleClick.bind(this)
  }

countdownClock = async (newRanNum) => {
const startingNum = newRanNum * 20;
for(let i = startingNum; i >= 0; i--) {
    await new Promise(resolve => {
        this.timer = setTimeout(() => {
         this.setState({
           timer: i
         })
        resolve()
     }, 1000)
   });
  }
}

handleChange(event) {
  this.setState({
    thought: event.target.value
  })
}

handleSubmit(event) {

}

generateInputs = (newRanNum) => {
    const inputs = []
    for(let i = 1; i <= newRanNum; i++){
      inputs.push(
        <Inputs type='text' onChange={this.handleChange}  className='textInputs' />
      )
    }
    return inputs;
  }

  handleClick(){
    clearTimeout(this.timer)
    let newRanNum = Math.floor(Math.random() * 20);
    this.countdownClock(newRanNum)
    this.generateInputs(newRanNum)
    let current = this.state.currentImg;
    let next = ++current % images.length;
    this.setState({
      currentImg: next,
      ranNum: newRanNum
    })
  }

  render(){
    let src = this.state.currentImg;
    return(
      <div>
        <Countdown name={'Countdown: '} countdown={this.state.timer} />
        <DisplayCount name='Word Count: ' count={this.state.ranNum} />
        <Image src={images[src]} />
        <Button onClick={this.handleClick} />
        <form onSubmit={this.handleSubmit}>
          <ul>
          {this.generateInputs(this.state.ranNum)}
          </ul>
          <input type='submit' value='Submit' />
        </form>

      </div>
    )
  }
}


ReactDOM.render(
  <Game />,
document.getElementById('root')
);

Upvotes: 0

Views: 1254

Answers (2)

Drake Xiang
Drake Xiang

Reputation: 368

There are generally two ways to do this:

  1. React way
  2. Old school DOM way

The second one should be familiar: just get all input DOM by document.querySelectorAll or something like that, and loop through the node list and get value via xx.value

The react way is a bit of verbose but maybe what you should do, the key is to make every input a controlled element, it means the value of input is passed by you and updated by you.

  1. So basically you should store all the values in your state, you probably should make it an array since your inputs'number is too many for hand writing and maybe uncertain
  2. Set onChange handler on every input or delegate this handler to ancestor for better design
  3. You have to come up a way to figure out which input you are updating, one simple solution is adding a custom html data-* attribute on every input and set it to current index, you can do this while generating inputs array. Then when you update, you get the input index from the change event and update to correspond position in value array

Upvotes: 1

Algef Almocera
Algef Almocera

Reputation: 819

Here's a sample script to give you an idea on how you can handle this. There are, of course, many approaches including using other libraries. This one doesn't involve using any other library.

See Demo here: https://jsfiddle.net/p0djaw3f/1/

Hope this helps!

Javascript

class SampleApp extends React.Component {

  constructor(props) {
    super(props)

    // Generate Random Number of Inputs. Min of 2
    const inputCount = Math.floor(Math.random() * 5) + 2
    const inputArray = []

    for (let ctr = 0; ctr < inputCount; ctr++) {
        inputArray.push('')
    }

    this.state = {
        inputs: inputArray
    };
  }

  // Capture changes from input[type=text] and update state
  onTextInputChanged(e, index) {
    const inputs = [...this.state.inputs]
    inputs[index] = e.target.value
    this.setState({
        inputs: inputs
    })

  }

  // Display Content in Text Area
  displayContent() {
    const content = this.state.inputs.join('\n')
    const target = document.getElementById('content')
    target.value = content
  }

  render() {

    return (
      <div>
        <h2>Random Inputs</h2>
        <ol>
        {this.state.inputs.map((input, index) => (
          <li key={index}>
            <label>
              <input type="text" value={input} onChange={e => this.onTextInputChanged(e, index)} /> 
            </label>
          </li>
        ))}
        </ol>
        <button onClick={this.displayContent.bind(this)}>Display Content</button>


        <h2>Display Inputs</h2>
        <textarea id="content"></textarea>
      </div>
    )
  }
}

ReactDOM.render(<SampleApp />, document.querySelector("#app"))

HTML

<div id="app"></div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin: 15px 0 15px 0;
}

input {
  margin-right: 5px;
}

textarea {
  height: 100px;
}

Upvotes: 2

Related Questions