Kousha
Kousha

Reputation: 36209

React hooks with dynamic array length

I have the following dummy example:

function App({size}) {
  const [values, setValues] = React.useState(Array(size).fill(''));
  function onChange(index, event) {
    console.log('setting index: ', index)
    console.log('to value: ', event.target.value);
    values[index] = event.target.value;
    setValues(values);
  }

  console.log('values are');
  console.log(values);

  const inputs = Array(size).fill().map((_, index) => (
    <input
      key={`input-${index}`}
      value={values[index]}
      onChange={e => onChange(index, e)}
    />
  ));

  return (
    <React.Fragment>
      {inputs}
    </React.Fragment>
  )
}

ReactDOM.render(
  <App size={3} />,
  document.getElementById('container')
);

I expect to be able to pass size to App and dynamically get that many inputs.

When I run this and type in the inputs, I see

values are
["", "", "", ""]
setting index: 3
to value: s
setting index: 1
to value: e
setting index: 0
to value: f

Looks like my component is not re-rendered. Why is that?

Upvotes: 3

Views: 5140

Answers (2)

Kousha
Kousha

Reputation: 36209

As @Nick mentioned, the problem was that I was directly mutating the state itself. I ended up using the following approach (instead of Nick's approach which does work):

function onChange(index, event) {
  const newValues = [...values];
  newValues[index] = event.target.value;
  setValues(newValues);
}

Upvotes: 2

Nick
Nick

Reputation: 16576

Your issue is most likely that you're trying to directly mutate state. In your onChange function, you should instead make sure not to try to mutate it.

function onChange(index, event) {
  const newValues = values.map((value, i) => {
    return i === index ? event.target.value : value;
  });
  setValues(newValues);
}

Upvotes: 3

Related Questions