Disable / workaround React key requirement? - reactjs

I'm getting the warning Each child in an array or iterator should have a unique "key" prop. Check the render method of Login
I want to pass an array of elements back, without using keys. I have to believe there's a workaround for this, without adding a pointless wrapper?
Note the return [<div/>, <div/>];
render () {
return (
<div className='login'>
{this.mobileWeb()}
{this.pcWeb()}
</div>
);
}
mobileWeb () {
if (this.state.isMobileWeb === true) {
return [
<div className='sky-container'></div>,
<div className='sea-container'></div>
];
}
}
pcWeb () {
if (this.state.isMobileWeb !== true) {
return [
<div className='sky-container'></div>,
<div className='sea-container'>
<LoginForm onChange={this.onChange} ref='loginForm' onEmailChange={this.onEmailChange} onPasswordChange={this.onPasswordChange} />
<input type='submit' value='Submit' onClick={this.login} />
</div>
];
}
}

There is a valid use case for not wanting to use keys, if you e.g. render strongly differing trees (think blog posts) or have a function that returns always the same array of items.
When you don’t provide an explicit key, React will use the index as key and emit a warning – however you would want to silence the warning, as using the index here is perfectly valid.
This doesn’t seem to be possible however, which has the sad consequence that you either have to make your code more complex for no reason (adding useless keys) or ignore the warnings (which means that valid warnings might be lost among the noise).

As of now, probably since 16.2.0 (November 28, 2017) you can wrap the elements inside <React.Fragment> instead of an array. The child components do not need keys although array of Fragments will still need keys.

you should not be passing the child element without key, React uses key for diff diff using virtual dom (it is lightweight javascript object). Never use key as Date.now() or Math.random(), because during render the react will see different key value and it will create new dom object.

A few years later, I will answer with newfound knowledge: the 'pointless wrapper' actually has semantic meaning to describe the collection and it's pretty normal to use a fragment eg <> ... </>, or a wrapper <div> to describe it, just not an array.

Edit: Do not do this, as the comment by #Jason Miller suggests.
Whenever it is straightforward, it is best to simply use the indices
of the array or some other ID. Sometimes, however, this is
impractical, for example, when a complex layout is generated
dynamically. A workaround for suppressing the warning is to generate a
random number as a key using key={Math.random()}. There are around 15 safe digits in the returned float and thus >10^15
possible outcomes, which should be safe enough for the most
applications.

Related

How to render values from object with arrays in react typescript

Hey everyone I am trying to render a few values inside an object with arrays but have a hard to doing so.
Here is an example of the object:
test {
arr1: [{alert: "this is alert 1", passed: true}],
arr2: [{alert: "this is alert 2", passed: false}],
arr3: [{alert: "this is alert 3", passed: true}]
}
So basically I want to render the alert and passed values. I tried using Object.keys and then inside mapping individual array based on the key but no luck.
Edit: Sorry if I my question wasn't clear but I was able to resolve it. Here is the code for anyone curious.
Object.keys(this.state.test).map((name, I) => (
this.state.test[name].map((tmp, idx) => (
<li key={idx}>{tmp.alert}</li>
<li key={idx}>{tmp.passed}</li>
))
))
The object is called Object, not Objects. Any browser console or build process would be telling you that Objects is not defined.
Additionally, you can't have multiple JSX nodes that aren't surrounded by a parent node. So that's a syntax error in the callback passed to .map(). Which should also be evident as a console error or build error, depending on how you run your application.
Either way, these errors are being reported to you somewhere. If you're not seeing them then now is a good time to take a step back and better understand your development process, because seeing error messages when things fail is critical.
Once these two things are corrected, your logic works just fine:
Object.keys(this.state.test).map((name, I) => (
this.state.test[name].map((tmp, idx) => (
<>
<li key={idx}>{tmp.alert}</li>
<li key={idx}>{tmp.passed}</li>
</>
))
))
Though you may want to re-think your approach a bit, because using the same key value for multiple nodes isn't going to make React happy. It's unlikely to cause any immediate problems, though may produce warnings/errors in the browser console. But you'd be better off changing the markup a bit to just use a single <li> and modify how you're displaying the two values within that element.

How to solve this Reactjs Eslint warning about 'index' is defined but never used

Feels like a mistake to me how can I not have an "index" when iterate a map.
Any ide?
You're not using/referencing index anywhere, so just remove it from the parameters. You don't need it.
You're not using the index returned from the map function. You're declaring 'id' inside of it and assigning a value returned from "uniqueId" somewhere else in your code.
This is not a bug.
If you don't want to see the warning, you could:
not use "index", since in fact you're not using it.
replace it with _, that way eslint will understand that you're discarding that parameter
Edit:
As pointed by Brian Thompson, you probably want to use the index anyway, to get a unique id without having to create one by yourself:
Resume.article.map((element, idx) => {
articles.push(
<div className="column" key={idx}>
...
</div>
)
}

Getting wordpress posts with react shows special chars instead of apostrophe

I am getting what I am assuming is json data from a wordpress blog endpoint like so:
https://example.com/wp-json/wp/v2/posts
I am looping through and showing the tiles for now:
<div>{posts && posts.map((post) => <h1>{post.title.rendered}</h1>)}</div>
But the post titles are not displaying properly. For example the word Don't shows Don’t
I have discovered that I can use dangerouslySetInnerHTML to fix this issue but is it safe? The fact that it has the word 'dangerously' in it is worrying.
I believe dangerouslySetInnerHTML is the way to go about this - but I will go into more detail as to why "dangerously" is in "dangerouslySetInnerHTML" and hopefully that will help you make an informed decision for your situation.
What dangerouslySetInnerHTML does is render any HTML string given to it within the DOM element.
For example:
<h1 dangerouslySetInnerHTML={{__html: post.title.rendered}} />
(as an aside, note the __html key has two underscores)
Will properly render the string Don’t to Don't.
This is all pretty harmless, however, if, for example, the value of post.title.rendered could be set by an untrusted party (such as an arbitrary user), and if this arbitrary user wanted to do some damage, they could enter a string such as:
<script type="text/javascript>
// Do evil stuff
console.log('I did some evil stuff');
</script>
This code would then be executed by the browser when the page loads - because React would have generated the following DOM:
<h1>
<script type="text/javascript>
// Do evil stuff
console.log('I did some evil stuff');
</script>
</h1>
So with all that in mind, if you are sure that the value of this field is within your control (and not anyone else's) and you also know that there will not be any arbitrary code in these strings, then go ahead and use dangerouslySetInnerHTML.
However, if there is the possibility that someone besides yourself could manipulate this field, I would instead look to something like decode-html-entities - this way you can have the presentation you want, without compromising your app/users.

Why does ternary in conditional class logic not provide the correct value?

I have a conditional class that should return the class name when true but it does not, even when true is returned. (I believe) This is due to the giant array of 40000 units I'm using to render a graph.
Fiddle
In the fiddle I want the class to be added to boxes 100, 101, and 102 on click. This should change the color.
This approach is a follow-up to another question that works in theory, but is not working in practice due to size of the array. I know this is absurd to have an array this large rendering a grid like this, but I'm invested and want to fix this. (But I'm open to other suggestions)
I want the below to add the class to some of the instances of the graph of 40000. When i matches and array of the boxes that should change color.
i is looping through 1...40000 while rendering a graph.
let indexes = [100,101,102]
<div className={'box ${indexes.includes(i) ? 'background-color' : 'null'}'}>
typeof(indexes.includes(i)) will be a Boolean.
I can see that true is returned and yet the class does not get added. However, if I try with a smaller array it works, so it seems it is a timing issue. setTimeout does not work here and causes the re-render to fail entirely.
The expected output should be, on document.querySelector('.box-container:nth-of-type(100'), 101, 102:
<div className='box background-color'></div>
But it is:
<div className='box null'></div>
Below is working example with some additional 'best practices' and corrections that will make you code more performant and readable...
https://jsfiddle.net/cantuket3/3cs7abyg/9/
(Oops, I provided a link to you example by accident initially. This one works)
(Also, I removed the inline snippets that were here a minute ago. The formatting that editor produced was completely illegible. Refer to jsFiddle above.)
You should move as much logic out of the render function as possible because every time React detects changes to state (which modify the UI) it will run the render function again. Also, makes your code more modular and readable...
render(){
return(
this.props.toRender.map(num => {
return <React.Fragment key={Math.random()}>
{this.renderBoxes(num)}
</React.Fragment>
})
)
}
TO
render(){
if (this.props.toRender && this.props.toRender.length) {
return(
<React.Fragment>
{this.renderBoxes()}
</React.Fragment>
)
} else {
return (
<div>
No Boxes yet!
</div>
)
}
}
This is unnecessary if you are defining this array imperativel. Just drop in the elements directly...
arr.push(100)
arr.push(101)
arr.push(102)
this.setState({
classesToAdd: arr
})
TO
this.setState({
classesToAdd: [10,11,12]
})
React only needs a 'key' when there a series of sibling elements, so it can optimize change detection.
<button key={Math.random()} onClick={this.updateState.bind(this)}>Add Classes</button>
TO
<button onClick={this.clickToAdd.bind(this)}>Add Class</button>

Render fields for Redux Form from values in an array

I'm creating a filter for a graph which contains several fields. Most of them are known fields, but one part is dynamically and that is which of the houses the user want to be included in the graph. The houses are contained in my state and is different for each user (basically, the user chooses what they are named). It's the houses part here I want to render dynamically based on the props.
The only example of this that I've found is this, but I haven't found a solution on how I can transition that to my problem. I thought I could just do something like this where every house field is placed in a array (like in that example):
renderHouseFields() {
const { fields: { houseArray } } = this.props;
return this.props.houses.map((house) => {
const houseField = (
<label
{...houseArray}
className="col-xs-9 control-label"
htmlFor="cottageCheckbox"
>
<input type="checkbox" />
</label>
);
houseArray.addField(houseField);
return (
<div key={house.name}>
<label
className="col-xs-3 control-label"
htmlFor="cottage"
>
{house.name}
</label>
{houseField}
</div>
);
});
}
but then I simply get this error message:
Warning: setState(...): Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
This is my first project in React so I'm quite sure I'm just overlooking something here, but I can't find the solution and would be grateful if someone could help me here.
(I'm also aware that I can upgrade to redux form 6 and use FieldArray, but I don't really want to do that in the middle of the project.)
Judging by your code I reckon you're getting the error, because you are adding to your houseArray directly in the render method. This will trigger an update to the props of your component, which should not occur in the render method, hence the error.
If you look at the Deep Form link you supplied, you'll notice that the only place modifications to fields are occurring, is within button event handlers.
In your case I think what you want to do is link the entries in your house array, to the actual checkboxes. Right now you're only adding the checkboxes, but it has no reference to a house field:
<input type="checkbox" name={house} />
Or maybe this, depending on the properties of house:
<input type="checkbox" name={`${house}.id`} />
On a side note, I really would recommend to upgrade to version 6, since the API makes a lot more sense and it contains a lot of improvements over the previous version. There's a migration guide: http://redux-form.com/6.6.1/docs/MigrationGuide.md/

Resources