The best way to remove a key from react component's state - javascript

I am using a react class component, which holds a state, with (lets say..) a lot of key-value pairs stored into it.
Upon user action (button press/toggle), I need to REMOVE / ADD a new key-value pair to the component's state. Adding one is relatively easy, but pulling out a key-value pair from the state can be done in several different ways, so I was wondering which one is best, most readable, most performant and most preferred by the ReactJS audience..
1) Option 1:
onRemove = key => {
const newState = this.state;
delete newState[key] // Interacts directly with the state, which might not be a good practice, or expended behaviour?
this.setState(newState);
}
2) Option 2:
onRemove = key => {
const {[key]: removedKey, ...newState} = this.state; // Defines two new variables, one of which won't be used - "removedKey";
this.setState(newState);
}
There might be more ways to do it, and I am wondering which might be the best one, that can be used in any circumstance, no matter how 'big' the state gets...
Please share your thoughts based on your work experience with React & State management!
Thanks!

When I do something like this, I generally do a modified version of your "Option 1." As you currently have it, Option 1 mutates the state object, which you shouldn't do. Instead, create a shallow copy of state and then delete the key.
onRemove = key => {
const newState = {...this.state};
delete newState[key];
this.setState(newState);
}
The reason I like this way over your Option 2 is subjective--It's very readable to me. Make a copy, delete a key. Simple and to the point.

Option 1 isn't an option. You can't directly modify state in React.
Option 2 is fairly standard practice.
A third (to my mind lesser) option is to remove the property after cloning the object, but deleting properties from objects isn't what JavaScript engines optimize for, so I don't see any advantage to it over Option 2.

Related

Redux best practice to filter data

in the process of developing an application, I'm facing a question about whether I'm using Redux correctly.
I have a fav:[] in which I add product objects and render their list. However, in order for the data not to be lost, I have to copy this fav:[] to favCopy:[] and only after that execute .filter
Example code:
case "fav":
state.fav = action.payload.filter === 'all'
? state.favCopy
: state.favCopy.filter((item: any) => item[type] === action.payload.filter)
break;
I would like to understand how right I am by keeping the original array intact? Maybe there is a way not to multiply arrays and use only one state?
We would recommend not doing filtering directly in the reducer most of the time. Instead, keep the original array in state as-is, and then also store a description of how you want the filtering to be done. From there, use selector functions to derive the filtered value as needed:
https://redux.js.org/usage/deriving-data-selectors

how to properly update an object or array in react state [duplicate]

This question already has answers here:
Why can't I directly modify a component's state, really?
(7 answers)
Closed 3 years ago.
Assume this is my state:
state={
user:{
name: 'Joe',
condition:{
isPrivate: true,
premium: false
}
}
}
And this is the methods I can use to update user:
updateUser = (property, value)=>{
// firstway. probably not a good one
let user = this.state.user;
user[property] = value;
this.setState({user})
// second way. probably the best way
let user = JSON.parse(JSON.stringify(this.state.user))
user[property] = value;
this.setState({user})
}
Although I know modifying the state directly is not a good practice but I'm getting the same result from both of them with no side effects so far.
So why should I take this extra step to copy the state then modify it on the copied object while this slows down the operation (however so little)!
So which one would be faster? what would be the side effects of the first method in the context of react? and finally what are the pros and cons of each method?
In response to your first method of updating state, you are getting a reference to the object nested in your state.
let user = this.state.user;
user[property] = value;
In this chunk you have already updated the state, so you are actually performing a side effect. The call to setState() just reflects those changes in the UI(i.e. re-rendering of the component).
The reason for not modifying the state directly might be some unintentional updates in the state. For example, if you want to make an api call by modifying some of the data in this.state and sending it as the body of the request(Note that you don't want these updates to reflect in the UI), then modifying the state directly like you did in method 1 could cause some unwanted changes in the state and subsequent calls to setState() might expose some unwanted changes to the user of the application.
However in your example it's fine to use any of those methods but it might not be a good practice.
Hope this helps!
The basic idea is avoid mutating objects, create new objects instead.
This mantra means that you should avoid direct mutations to javascript objects that you have in memory but, instead, you should create a new object each time.
You can use the ES6 spread operator in order to get a clone of your object. With the spread operator you can also update the properties of the clone, so that you perform the required update to the object properties.
This is the code you need:
updateUser = (property, value) => {
const user = {...this.state.user, [property]: value}; // gets a clone of the current user and update its properties
this.setState({ user });
}
The three dots in the syntax above are not a typo, they are the aforementioned ES6 spread operator
Based on my knowledge (I'm quite new to react) there are basically three reasons to avoid direct state mutation:
recalculating a new state each time is simpler than trying to update an existing state. When I say simpler I mean simpler from a conceptual and coding perspective. Creating a new object each time avoiding any kind of side effect will simplify your code and will reduce your bugs.
you can't be sure on how your component and its children components are using a given piece of state. That piece of state is used by your component and could be passed to its children components via props. If you only reason on your component in isolation you can't know how the children components are using that piece of state. What's gonna happen when you mutate the object in memory by changing its properties ? The response is who knows. You can have a series of side effects and, more important, you cannot be sure about what kind of side effects you will get if you reason only on your component in isolation. It really depends on how the component hierarchy is composed. Reasoning about side effects is always a mess, it's too risky to cope with them and a better approach is trying to avoid them.
react and react dom have been designed to update the browser DOM efficiently when the state is mutated by following the best practices of functional approach (no side effects and no direct state mutation). This means that, if you use react the way you are suggested to, react itself will have a better time in calculating the modifications to be applied to the DOM in order to redraw your components and then your app will perform better.

React list with no keys

I have an array of number that I wish to render in a tabular form. The array is returned from an API call, not generated by my app.
The data may change but is unlikely to do so, and in any case there are only twenty odd values, so re-rendering the whole table is not really a problem.
A simple data.map(value => <td>{value}</td> should do it.
But I keep getting an Each child in an array or iterator should have a unique "key" prop. warning. Is there any way that I can tell React that there is no key and that I wish it to re-render the whole table if anything changes.
Alternatively, is there any way that I can generate a unique key for each entry? The data items are not guaranteed to be unique.
I should add that I understand what keys are for and why they are useful, but in this instance I do not have any and the easiest thing would be not to use them, since there is unlikely to be a re-render.
You can use the index as the key. I think its worth reiterating that using the index as the key only works fine in the very specific scenario that the OP is facing.
This is particularly annoying when requirements change and all of sudden the list is being modified. This shows up as items not being updated during the render because the item updated has the same key (the index), its value is different, but react only cares about the key.
In cases where your data has no unique key. You should use some function that generates a unique id for each item. A simple version of that function just increments a global counter:
// Declared globally (as in attached to window object or equivalent)
var myuniqueidcounter = 0;
function uniqueId() {
myuniqueidcounter += 1
return myuniqueidcounter;
}
// Do this in the props change or whereever your data gets passed in
let keyedData = data.map(value => Object.assign(value, { Id: uniqueId() });
// In render
data.map(value => <td key={value.Id}>{value}</td>
That way, on multiple render calls, the ids returned are always unique. We assign the key when we get the data to avoid having to re-render the entire list on each call to render().
However, this case is actually pretty rare as you can usually find some combination of the backing data that will produce a unique key for each entry.
If you do go index-as-key
This article lists 3 conditions that should be met when choosing index-as-key approach that I think is a good check list:
The list and items are static–they are not computed and do not change;
The items in the list have no ids;
The list is never reordered or filtered.
data.map((value,index) =>{
<td key={index}>{value}</td>}
)
or
data.map((value,index) =>{
let i = Math.floor(Math.random() * 1000+1)
<td key={i}>{value}</td>}
)
You can use index as your key as it is unique each time
Based on the question asked, it might be worth saying that there is an another solution to this that doesn't use keys:
e.g. The following will complain about not having unique keys:
React.createElement('div', {}, [<span>1</span>, <span>2</span>]);
However, the following renders all children with no problems (This is what JSX transformed to JS looks like for nodes with multiple children):
React.createElement('div', {}, <span>1</span>, <span>2</span>);
So if you have e.g. a smallish list of generated react element fragments and unique keys don't offer and advantage in your situation, you can do:
React.createElement.apply(null, ['div', {}, ...elementList])
Notes:
elementList is passed as arguments to React.createElement which might be an issue if the list is huge.
It will re-render all the children with each render.
Using unique keys is generally the recommended approach, and is more performant for re-rendering.
However there are occasions where you just want to render in a single shot and don't care about re-rendering, or the data is not structured in a way that you can make good use of unique keys. You can use this as a work-around if you really need to.

(React.js )Initial state generated randomly. How to prevent it from regenerate each time I handle an event?

I am building a game that players can attack each other by turn. So first I set the name, jobmanually and generate life,damage,magic randomly in componentWillMount().
I hope that every time I submit the attack form, certain amount of life with be reduced from the attacked person. But now every time I submit, the whole state is regenerated(with all kinds of bugs).
Can I do something to solve it?
app.js: https://ghostbin.com/paste/ype2y
attack.js: https://ghostbin.com/paste/wzm3m
I noticed that you do a lot of:
let players = this.state.players
which you are not supposed to do. Array is an object in js so here you are passing by reference. This means that every modification to the var players actually has side effects and modifies the state which you should never do. I generally recommend to never use in-place operations like splice, and to always use a copy of the state. In this case you can do:
let players = this.state.players.slice()
and from then on any modification to the players var does NOT affect the state. Double check you are not doing this anywhere else in your code. On top of that you should use the constructor only to set up and initiate your state. Otherwise every time the componentWillMount method is called your state is regenerated which is probably not the behavior you are expecting.
EDIT
I figured I could give you more pointers for what you are trying to do with arrays, as a general rule of thumb I follow this approach. If my new state has an array field which is a subset of the previous one then I use the .filter method, if the array of my new state needs to update some of its entries then I use the .map method. To give you an example on player deletion, I would have done it this way:
handleDeletePlayer(id) {
this.setState(prevState => ({
players: prevState.players.filter(player => player.id !== id)
}));
}
Your initial state should be generated in the constructor. This is done only once and will not be repeated when components props are updated.

React: how to compare current props.children with new one

Hi,
i am building component which acts only as wrapper for some other generated content and uses third party library. This library works with props.children of component. So far so good, but this thrird party library is little laggy when applied, or refreshed on element. And because only reason to refresh this library is when props.children changed I am trying to figure how to compare this.props.children and nextProps.children in shouldComponentUpdate. I was thinking that PureRenderMixin should do the work, but for me it does not works. Component is rerendered even if I change only state.listName as it is in example below.
<div>
List name '{this.state.listName}'
<br />
<MyComponent>
<ul>
{listOfLi}
</ul>
</MyComponent>
</div>
Is there any way, how to manage comparing of props.children or any other option how to do something like that?
Thanks for any help!
As Matt S pointed out, the accepted answer is kind of a fragile workaround and would depend on a non-standard use of key. Aside from the list examples he listed, even using something like key={id} would fall down if your ids remained the same but certain fields were modified in the resources they represent.
This issue contains a good discussion on the topic and ends with a more stable workaround. Essentially, you can simplify the children prop in a way that allows you to run a deep comparison. You can use the React.Children utilities to write the simplification methods:
// Flattens all child elements into a single list
const flatten = (children, flat = []) => {
flat = [ ...flat, ...React.Children.toArray(children) ]
if (children.props && children.props.children) {
return flatten(children.props.children, flat)
}
return flat
}
// Strips all circular references and internal fields
const simplify = children => {
const flat = flatten(children)
return flat.map(
({
key,
ref,
type,
props: {
children,
...props
}
}) => ({
key, ref, type, props
})
)
}
Then you can use shouldComponentUpdate or React.memo to prevent re-renders:
const MyComponent = ({ children }) => (
<div>{ children }</div>
)
export default React.memo(MyComponent, (prev, next) => (
JSON.stringify(simplify(prev.children)) ===
JSON.stringify(simplify(next.children))
))
These utilities + JSON.stringify are just one approach, the one mentioned in the comment is similar and you could also leverage utilities like lodash.isequal for the deep comparison. Unfortunately I don't know of any one or two liners for this comparison but please comment if you know a simpler stable way to do this!
You can make use of child key prop that React suggests that arrays of children should be given to uniquely identify them. Because each child has a key, you can reliably tell whether children has changed across prop changes (this is the entire point of keys!). If the keys don't match between new and old then they have changed.
React.render(<App><Child key='1'/><Child key='2'/></App>, document.body)
and do the check in the App component you want to check before each update if the children changed
shouldComponentUpdate(nextProps){
var oldKeys = this.props.children.map( child => child.key);
var newKeys = nextProps.children.map( child => child.key);
//compare new and old keys to make sure they are the same
}
Note that this doesn't tell you if the content of each child has changed, you have to compare each by some criteria (such as deeply comparing props) if you want to know if nothing in the whole tree below this point has changed
as an even further optimization we know that children will never change as result of a state change so we can actually do our comparing in componentWillReceiveProps() and just set some state property like childrenHaveChanged
Something about the design and behavior you describe is off. You should rarely, if ever, have to concern yourself with performing manual diffs of children. That should be left to React. If the library you are using is choking every time the component updates, regardless of whether it's because of children or some other state/prop change, it is not written reactively and you should look for a different one that is written reactively. Or you could contribute to fixing that behavior in the open source project, either by opening an issue or submitting a pull request.
It's not easy to do what you're trying to do because it shouldn't be necessary. React is very, very good at handling reconciliation and will only render when something has actually changed that will change the state of the DOM relevant to it.

Categories

Resources