Search code examples
javascriptarraysreactjsobjectuse-reducer

ReactJs : useReducer on array of object. I need more understanding


  • Still new to the world of Js and React, I just made a website that works. But it bothers me the way I made it work. And I would like a bit of insight into how I could do things the better way. So here I made a simple project on the sandbox to reproduce what I did and ask a few questions. Here is the sandbox: https://codesandbox.io/s/amazing-dream-oz8ec?file=/src/App.js

You will find a console.log(tab) to keep track of the evolution of the tab.

  • So here is a quick explanation of the project, I have two components (Mario and Luigi) that I want to interact with and react (no puns intended) with modification on one another. For this, I made an array of objects and put the characteristics of said components in each object. I then make changes to the array of objects with cases that I built in my reducer. From another point of view, I have Mario and Luigi, I want to be able to click on one of them to make them able to jump (by creating a 'jump' buttons that I want exclusive in term of visibility to one of them), then click on the 'jump' button to make one of them jump (which make the one jumping appear full screen on the webpage and the other one invisible for the time being). And the last thing I want an 'exit' button (that appears on while jumping) which will reset my value (of the array) to the original ones.

So here is where I get kind of lost.

  • First question: So I kind of get the difference between mutating an existing state (no re-render) and returning a new one (actually getting a re-render). In my first case 'toggleCanJump' the mutation (tab.map(hero => (hero.canJump = false));) is affecting the return since it makes my tab's canJump value exclusive (only one of them can be true at a time), whereas in my second case 'toggleJumped' the mutation (tab.map(hero => (hero.canJump = false));) is not affecting the array, hence why in the className of the button I have two conditions to establish the classes (className={hero.canJump && !hero.jumped ? "btn flex" : "btn"})?

To verify that just comment on the mutation before they return in the first case 'toogleCanJump' and you will see that it is not exclusive anymore both 'canJump' can have the value true. Whereas even with the mutation, I can't achieve to have the value of 'canJump' reacting to triggering 'jumped' even with the mutation that is working as stated just before.

  • Second question: Which is kind of redundant with the first one. Since I don't seem to get full control of my array of objects, when one the components is full screen and the other one displayed as none (for example: when Mario is jumping and so Luigi disappear). I need a 'reset' button to actually toggle a case that returns (literally copy and paste) the initial value of my an array of object. I find it clumsy but since I don't fully grasp the manipulation of my array (as stated in question one) I found this solution to make it work. So how would I be able to slice and re-insert properly my object in the array ?

This question is kind of the same as the first one but on a different scenario. I feel like I get the differences between mutating and returning my state (array of object), but I don't seem to be able to make it effective. Mutation on 'toggleCanJump' seem to work but on 'toggleJumped' actually does not, and is returning the tab as I declared it really the best way (more elegant one) to reset it ?

I would like to add that may be it is my logic that is not the good one ! Maybe it should be an array of arrays, I find object to be more talkative for future updates of the website. but I am not against doing it with an other manner.

If you feel like having a solution, or an alternative way of doing things. Your time would be much appreciated. Thank you for reading.


Solution

  • See reconfigured sandbox:

    https://codesandbox.io/s/cold-frost-x62x3?file=/src/App.js

    In regard to the first question, you are actually using map as though it is forEach and directly mutating state. Map is intended to return a new array, where as you are equating values to the original array by using "=".

    The fact that only one appears to effect the outcome however, is an illusion (they both do) and caused by a different reason. Your dispatches were being set off multiple times -- some odd, some even: counter balancing the effect. This was happening for 2 reasons: 1. your reducer wasn't placed outside your function, and 2. your buttons were nested and therefor required e.stopPropagation().

    In regard to question two, because your hero.style property is unique, you would need to create an object map to restore it. Its easier to keep it as since the characters are already hard coded into the initial state.