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

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.

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.

Related

Is this a valid use case for derived state from props? [react]

React documentation seems to be very insistent on the idea that in almost every situation, deriving state from props is a bad idea, an anti-pattern, verbose, likely to cause bugs, hard to understand, and all-around probably going to place a curse on one's lineage for a thousand years.
My use case isn't that weird, so I'm probably doing something wrong, but the suggested patterns for not needing getDerivedStateFromProps() (i.e. making Your object fully controlled or fully uncontrolled) don't seem like good solutions.
The situation: I have a Table component, that takes in an array rows as a prop. Table is used in many different places in my app. I want the Table to be able to handle row-sorting. It is obviously a bad idea to to make whichever parent component controls Table to have to control the sorting*, so fully controlled is out. Making Table fully uncontrolled with a key, also seems like it doesn't make a lot of sense unless the key is the row-data itself-- but my understanding is that key is meant to be simple data (like an id), and actually having to compare all of the rows, which are typically fairly complicated objects, would be pretty inefficient**. Using memoize-one is also not an option as I am working in a closed system and can't import any new libraries.
My current solution: Table has a state variable sortedRows which is updated either whenever sort() is called or whenever props.rows is updated (via getDerivedStateFromProps), by:
Making a shallow copy of props.rows,
in-place sorting that copy and
updating state.sortedRows on that value.
As I see it, there is still only one source of truth here (which is from props), and the state is always just storing a sorted version of that truth (but always dependent on and in sync with it).
Is this solution bad? If so why? What would be a better way to implement this?
Thanks!
Note: I didn't include my code because I am massively simplifying the situation in this prompt-- in reality Table element already exists, and is pretty complicated.
Note 2: I going to ask if I'd run into issues once I want to be able to modify elements in the tables, but I think I'm actually ok, since Table doesn't manage its elements, just arrange and display them, and the buttons for adding and removing elements from a table are not contained within Table, so all that processing is happening at the level of the parent's logic as passed down as part of props.rows
*Having something like <Table rows={sort(rowsFromParent)}/>every time I call Table is repetitive and error-prone, and since clicking on a table header determines sorting column, we'd actually have to have the parent element passing down an onClick() function in every case, which quickly and unnecessarily ramps up complexity).
**There is also a secondary problem to do with rebuilding an element. The table has an infinite scroll, such that when You reach a certain element more rows are loaded in. Using key will destroy the Table component and create a new one, scrolling the user to the top of the new table (which could potentially have many thousands of rows). Something also feels wrong about having to set key in each use of Table, even though resetting based on changes to props.rows seems like it should be intrinsic to how Table works, rather than something that has to be configured each time.
Edit: I have React 15.4, which is before getDerivedStateFromProps was added and using a later version is not an option, so I guess even if I happened to find a valid use case for getDerivedStateFromProps, an alternative would be nice...

Clear Blockly workspace without moving items to trash

I would like to use code to clear a Blockly workspace, and in some cases overwrite with new Blockly XML. The issue I am facing, however, is that Workspace.clear() moves current items to trash, which can be annoying when trying to reset a workspace, or overwrite it with blocks (which means clearing it first, as otherwise the current blocks would be overwritten). I have found that I am able to clear the trashcan with workspace.trashcan.contents = [], but this results in some unexpected behaviour regarding the rendering of the lid, which appears slightly ajar despite it being empty.
Even if I could find a way to clear the trashcan, this would still result in clearing all previous items in there, while all I'm looking for is to do a single clear action without impacting the trashcan. I've tried looking at docs for Workspace.Clear, but they don't seem to give any semantic way to do this. I've also tried looping through Workspace.getTopBlocks() with Events.Delete, but that also results in moving items to the trashcan.
I've also tried to implement a manual solution, something along the lines of:
const contentsBeforeClearing = workspace.trashcan.contents_;
workspace.clear();
workspace.trashcan.contents_ = contentsBeforeClearing;
For the first line, I've also tried Object.assign([], workspace.trashcan.contents_) and workspace.trashcan.contents_.slice(), but in all three cases (including the top one), the end result is equivalent to simply calling workspace.clear. I'm guessing this is something to do with getter-setters manipulating contentsBeforeClearing (as the variable's value has changed if you test it in the console after the clearing to have the deleted block(s)), but how to get around that is beyond me.
How can I clear the workspace without manipulating (not just resetting) trashcan contents?

Store the complex object and maintain it across different state

I am having List<Rules> collection which contains the rules, clicking one of these rule will open one Pop-Up.
Pop-Up will contain two button Update & Cancel.
On click of Update, I am calling an API which will do needful task.
My concern is Rules will also contain set of different object, and each of having the nested child objects.
On Pop-Up whenever I try to edit particular things inside the Rules, Again there is Update & Cancel button to store that particular step.
So it is the chaining of object which I must need to remember so that on cancel click I am able to retain its previous step.
I have object of type Observables.
rules$: Observable<Array<Rules>>
I want the best possible solution to maintain the chain-of-objects of Rules while editing the Rules.
I have tried by implementing:
Stack
But I think there can be better way to solve this.
Any type of input will be useful.

Algorithm to update only the changed innerHTML of the document

I have 2 JS variables. before and after. They contains the SAME html document, but have some modification. About 1%-10% change between them. I want to update the body from before to after. The variablesbefore and after are raw string.
I can do something like that:
document.documentElement.innerHTML=after
The problem is that if I render this way it not look good. The render takes time, and there is a white screen between the renders. I want to show the user 10 modification in a second (video of modifications)
So what I want to do. I want to search and find only the elements that changed only by analyze the HTML text of before and after.
My way of solution:
I can find the changes and the position in the text using Javascript Library for diff & match & patch.
The question is:
After I find the text changes. How to find only the elements who changed. I update only those elements.
I thought, maybe to create a range, that contains every change, and update the range, but how exactly to do that?
If anything unclear, please comment, I will explain better.
I found a very good library for it: https://github.com/patrick-steele-idem/morphdom
Lightweight module for morphing an existing DOM node tree to match a
target DOM node tree. It's fast and works with the real DOM—no virtual
DOM here!
Very easy to use, and doing exactly what I need
If I have understood your question correctly, then what I would have done is,
1) Make a new object (view Object) which will control the rendering of DOM elements. (Similar to MVC)
2) In this object, I would have created 3 functions.
a) init function (contains the event-handlers)
b) render1 function (which will contain elements in before element)
c) render2 function (which will contain elements in after element)
Whenever there is an event where I need to change the HTML of a class/id/body/document, I will change that in init function and call render2 function which contains the after element.
This should not give any error, however the browser has to work to render all the page, but rendering can be divided over multiple elements of document. So, whenever you need to render a part of document, make separate render functions.
p.s. there can be different approaches.
You must implement the LCS(Longest Common Subsequence). To understand better of this algorithm you can watch this youtube video. Also It's easier to first study Longest Common Substring.
I think I have a solution. virtual-dom can do the work for me. I can create two VTree, make a diff, and apply a patch.
From the documentation of virtual-dom:
virtual-dom is what I need.
Manual DOM manipulation is messy and keeping track of the previous DOM
state is hard. A solution to this problem is to write your code as if
you were recreating the entire DOM whenever state changes. Of course,
if you actually recreated the entire DOM every time your application
state changed, your app would be very slow and your input fields would
lose focus.
virtual-dom is a collection of modules designed to provide a
declarative way of representing the DOM for your app. So instead of
updating the DOM when your application state changes, you simply
create a virtual tree or VTree, which looks like the DOM state that
you want. virtual-dom will then figure out how to make the DOM look
like this efficiently without recreating all of the DOM nodes.
virtual-dom allows you to update a view whenever state changes by
creating a full VTree of the view and then patching the DOM
efficiently to look exactly as you described it. This results in
keeping manual DOM manipulation and previous state tracking out of
your application code, promoting clean and maintainable rendering
logic for web applications.
https://github.com/Matt-Esch/virtual-dom

Is this normal for AngularJs filtering

I'm pretty much new to angular, but I feel like this is kind of crazy.
I've got multiple collections being displayed via ng-repeat in one controller scope. There's an input field for each list to perform a simple query. I was playing around with various filters in my code and I ended up putting a console.log in my filter function. I realized that every time my filter function was called for one list, it was being called for all of the lists in the scope. Furthermore, it was calling the filter function twice each time. So with 3 collections, filtering one of the lists would call the filter function 6 times.
I thought maybe it was just my custom filter, so I tried it out on the default filter function. Same story. Here's my code:
https://dl.dropbox.com/u/905197/angular-filter-test.html
Go to the console and see for yourself :/
What am I doing wrong here? This seems like such a simple thing but it's doing so much work.
This is normal, angularjs uses a 'dirty-check' approach, so it needs to call all the filters to see if any changes exist. After this it detects that you have a change on one variable (the one that you typed) and then it re-executes all filters again to detect if it has other changes.
See the first answer of this question

Categories