0

I am new to react-native and i want to update data in objects array! Here is the array :

  const questions = [
    {
      question: "What kind of fruit was used to name a computer in 1945?",
      answers: [
        { id: "0", text: "192.168.1.1", correct: false, userInput: false },
        { id: "1", text: "127.0.0.1", correct: true, userInput: false },
        { id: "2", text: "209.85.231.104", correct: false, userInput: false },
        { id: "3", text: "66.220.149.25", correct: false, userInput: false },
      ],
    },
    {
      question: "What kind of fruit was used to name a computer in 1984?",
      answers: [
        { id: "0", text: "Blackberry1", correct: false, userInput: false },
        { id: "1", text: "Blueberry", correct: false, userInput: false },
        { id: "2", text: "Pear", correct: false, userInput: false },
        { id: "3", text: "Apple", correct: true, userInput: false },
      ],
    },
  ];

It is random text. I want to update userInput (when i press a button) value of the first answers object like -

questions[0].answers[0].userInput = true;

Thats of courst not works without hooks so i change const questions with

const [questions, setQuestions] = useState([here i paste object array above])

And i wrote following code to check if value changed when i press a button. So code in the button onPress event is:

questions[0].answers[0].userInput = true;
setQuestions([questions]);
console.log(questions); // to check outeput

Nothing of course happened and this object (questions[0].answers[0].userInput = true; ) is not changed So how can i fix that? I think i should create copy of the array and then changed this value. I saw this lib that can help me but i dont know how to start?

Nikola Marinov
  • 223
  • 1
  • 11
  • If you're defining the data structure here you might separate your immutable data from the mutable, in your questions data you could remove the userInput parameter and store that separately in state since it's only one item from the whole that you want to change, I dont know your overall architecture so it's hard to say what the best way to store/ pass the user input would be. – b.stevens.photo Jul 04 '21 at 21:17

1 Answers1

2

Whenever you're trying to use state values in the left part of assignment operation, there's a big chance something goes wrong. However, you don't need to copy the whole structure either. Essentially, you need to do something that React casually does with DOM - provide new values only for the parts that should be updated - but with your data this time.

Here's one possible way to do that (assuming you want to change value of answer a for question q, changing its userInput to true):

const newQuestions = questions.map((question, i) => {
  if (i !== q) return question; // non-affected questions will have the same ref

  const newAnswers = question.answers.map((answer, j) => {
    if (j !== a) return answer;
    return {
      ...answer,
      userInput: true
    }
  });

  return { 
    ...question, 
    answers: newAnswers
  }
});
setQuestions(newQuestions);

In this case, there are three changes of references in your state after the update:

  • new array reference is passed to setState
  • in this array, only updated element is the one with updated answers; once again, it has a new array reference stored in its answers property
  • this new array mostly consists of the same elements; only the one with userInput prop changed is actually replaced

Admittedly, there's a lot of code, and it's easy to get lost there. Thankfully, there's a lot of tools simplifying those data manipulations. That's, for example, how the same code might've been written with Immer:

const nextQuestions = produce(questions, draftQuestions => {
    draftQuestions[0].answers[0].userInput = true;
});
setQuestions(nextQuestions); 

What? Yep, that's it. Here's a telling quote from Immer docpage:

Using Immer is like having a personal assistant. The assistant takes a letter (the current state) and gives you a copy (draft) to jot changes onto. Once you are done, the assistant will take your draft and produce the real immutable, final letter for you (the next state).

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • Should i install something for ' Immer'? – Nikola Marinov Jul 04 '21 at 21:33
  • trying using immer and it changed after second clicked on the button? First show the array with old value and on the second click it changed – Nikola Marinov Jul 04 '21 at 21:43
  • I used again console.log(questions). It looks like setQuestions has something like delay and it prints the array and then update it – Nikola Marinov Jul 04 '21 at 21:47
  • When you call `setState(something);` and then in same function console.log `state`, you _will_ see the old value. That's how useState works. – raina77ow Jul 04 '21 at 21:48
  • Ye is it possible to ifx that? or any suggestions to avaid – Nikola Marinov Jul 04 '21 at 21:48
  • Why should you fix this - and how exactly you expect _const_ value to change? You shouldn't care about the previous state value anyway. Check [this thread](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) for more details. – raina77ow Jul 04 '21 at 21:49
  • Ye i am so stupid ! Thank you so much and sorry for this questions but i am new to react-native – Nikola Marinov Jul 04 '21 at 22:01