1

Guys i have this example code bellow:

const [data, setData] = useState([{question: '', option: ['']}]);

Then data and setData will pass to my component, like:

<Question setData={setData} data={data}/>

My code inside Question component is:

const handleAddOption = (questionIndex: number) => {
    let newArray = data;

    newArray.map((item: any, i: number) => {
      if (i === questionIndex) {
        newArray[questionIndex].options.push('');
      }
    });

    setData(newArray);
  };

The problem is, if i add a new entire Object it will "refresh" my page and show, but, when i add like the last lines of code, only a new string inside the array, it will not "re-render".

Anyone knows how to solve this?

skyboyer
  • 22,209
  • 7
  • 57
  • 64

3 Answers3

0
  1. It seems like You have typo write newArray[questionIndex].option.push(''); instead of newArray[questionIndex].options.push('');
  2. But if it doesn't help try forceUpdate(); more details You can find in this answer How can I force component to re-render with hooks in React? or try to use this package https://github.com/CharlesStover/use-force-update

Good Luck :)

Robert Hovhannisyan
  • 2,938
  • 6
  • 21
  • 43
0

Rough implementation


I think you can change:

const [data, setData] = useState([{question: '', option: ['']}]);

// into

const [questions, setQuestions] = useState([]);

And as you receive new question objects, you can do setQuestions([...questions, newQuestion]).

That is, assuming, you have a form that is receiving inputs for new questions. If this is the case, in your onSubmit function for your form, you can generate your new question object and then do setQuestions([...questions, newQuestion]).

khan
  • 1,466
  • 8
  • 19
0

In react first rule of thumb is don't mutate state directly. It works for class-based components and setState, it works for redux's reducers, it works for hook-based useState too.

You need to

setData((data) => data.map((item, index) => {
  if (index !== questionIndex) return item;
  return {
     ...item,
     options: [
        ...item.options,
        ''
     ]
  };
}));

There are several items to highlight here:

  1. as well as for class-based component's setState there is possible to provide callback into updater function. I better skip describing it in details here. May suggest to take a look into SO question and ReactJS docs. Yes, it's not about hooks but it uses the same logic.

  2. We need to merge our old data with changes to keep properties we don't want to change still present. That's all this spread operator is so hardly used. Take a look into article on handling arrays in state for React

  3. Last but not least, we have check to directly return item if it's not we want to update without any change. This makes us sure no related children components will re-render with actually exact the same data. You may find additional information by searching for query "referential equality"(here is one of article you may find).

PS it may look much easier to force update instead of rewriting code completely. But mutating state directly typically later ends up with different hard-to-reproduce bugs.

Also you may want to change components hierarchy and data structure. So no component would need to traverse whole array to update single nested property.

skyboyer
  • 22,209
  • 7
  • 57
  • 64