MultiSelect in material ui not selecting values properly - reactjs

I need help with the following code.
The values being selected are messed up. They are not right. Some of the elements get selected twice or more times. some don't get selected at all.
function HelloWorld({
greeting = "hello",
greeted = '"World"',
silent = false,
onMouseOver,
}) {
if (!greeting) {
return null;
}
// TODO: Don't use random in render
let num = Math.floor(Math.random() * 1e7)
.toString()
.replace(/\.\d+/gi, "");
return (
<div
className="HelloWorld"
title={`You are visitor number ${num}`}
onMouseOver={onMouseOver}
>
<strong>
{greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()}
</strong>
{greeting.endsWith(",") ? (
" "
) : (
<span style={{ color: "grey" }}>", "</span>
)}
<em>{greeted}</em>
{silent ? "." : "!"}
</div>
);
}

Related

Each child in a list should have a unique "key" prop error despite method to create unique key

In my application I am currently getting the react warning:
Warning: Each child in a list should have a unique "key" prop.
Check the render method of GetReplies.
This is the GetReplies method:
export function GetReplies(props) {
const Id = props.Id;
const replies = allComments.filter(obj => obj.ParentCommentId === Id).
sort((objA, objB) => new Date(objB.Time) - new Date(objA.Time));
console.log(generateKey(Id));
if (Object.keys(replies).length !== 0) {
return (
<div key = {"replies-container-" + generateKey(Id)} id={"replies-container-" + Id} className="replies-container">
<div key ={"panel-heading replies-title" + generateKey(Id)} className="panel-heading replies-title">
<a key = {"accordion-toggle replies-" + generateKey(Id)} className="accordion-toggle replies-a collapsed" data-parent={"#replies-container-" + Id} data-toggle="collapse" data-target={"#replies-for-" + Id}>Replies</a>
</div>
<div key = {"replies-for-" + Id} id={"replies-for-" + generateKey(Id)} className="replies-list collapse">
{
<React.Fragment>
{ Object.entries(replies).reverse().map(([key, arr]) => {
return (
<GetComments commentsArray = {replies}/>
)
}) }
</React.Fragment>
}
</div>
</div>
);
}
}
and this is the GetComments Method it calls:
export function GetComments({ commentsArray }) {
return (
<React.Fragment>
{commentsArray.map((comment) => {
const localId = comment.LocalId;
const parentCommentId = comment.ParentCommentId;
const parentLocalId = allComments.filter(obj => obj.Id === parentCommentId);
const recipients = comment.Recipients;
let recipientsArray = [];
let recipientsList;
recipients.forEach(function (arrayItem) {
recipientsArray.push(arrayItem.Name);
recipientsList = recipientsArray.join(', ');
});
console.log(generateKey(localId));
const date = new Date(comment.Time);
const formattedDate = date.toLocaleDateString() + " " + ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2);
return (
<div key={generateKey(localId)} className="comment-container">
<div key={generateKey(comment.Commenter.ItemId)} className="commenter">
<span className="id-label">{localId}</span>
{parentCommentId && (
<span className="reply" title={`in reply to ${parentLocalId[0].LocalId}`}>
<a className="reply" href={"#c" + parentLocalId[0].LocalId}>⤵</a> </span>
)}
<span><a id={"c" + localId} name={"c" + localId}>{comment.Commenter.Name}</a></span>
<div key={generateKey(localId) + "-comment-actions-container "} className="comment-actions-container">
<button type="button" className="btn-reply" data-value={comment.Id} title="Reply to comment" data-toggle="modal" data-target="#dlg-new-comment">⥅</button>
</div>
</div>
<div key={generateKey(localId) + "-recipients "} className="recipients">{recipientsList}</div>
<div key={generateKey(localId) + "-comment "} className="comment">{comment.Comment}</div>
<div key={generateKey(localId) + "-comment-footer "} className="comment-footer">{formattedDate}</div>
<GetReplies Id = {comment.Id}/>
</div>
);
})}
</React.Fragment>
);
}
To help make sure each key is unique I made this generate key method:
const generateKey = (pre) => {
return `${ pre }_${ new Date().getTime() }`;
}
However I am still getting that unique key error and I have no idea what it is I could be missing?
I am even tried to expand upon it by doing:
const generateKey = (pre) => {
let index =0;
return `${ pre }_${ new Date().getTime()}_${index++}`;
}
but index would always equal 0? So I'm not sure why that wasn't incrementing either, any advice would be appreciated
Even though your generateKey might work (not sure though since it will get called quite quickly when mapping over an array) you are adding the key to the wrong part of your code.
React expects a key on every item in a for-loop. In your case you have added a key to every JSX element, except for those within the Object.entries(replies).reverse().map
You could probably fix your problem by adding keys in there, like so:
{Object.entries(replies).reverse().map(([key, arr]) => {
return (
<GetComments key={key} commentsArray = {replies}/>
)
})}
To add a note though, React uses the key value to recognize changes on re-renders. In case your array gets re-ordered it can use the keys to prevent enormous re-renders. The best practice here would be to add a key that is related to the data itself, not the location in an array or a random number.
In addition of the previous answer :
const generateKey = (pre) => {
let index =0;
return `${ pre }_${ new Date().getTime()}_${index++}`;
}
will always have a 0 index, because each function call will create his own scope, with his own "index" variable. To increment the index on each call, you must extract the "index" variable outside of the function, so each function call will share the same index variable, from the 'outside' scope (but using the item's index remains the best idea, if you have no unique key you can use from your data).
Using "new Date().getTime()" to generate keys is also a bad idea, because your code will be ran so quick that a few components could (and will) share the same timestamp.
An alternative is to use a third party library, like 'uuid', to generate unique ids, but it must be used carefully.
You should use the index of the commentsArray.map
like :
{commentsArray.map((comment, index) => {
...
key={generateKey(Id, index)}
and then :
const generateKey = (pre, index) => `${pre}_${index}`;
You are filtering with Id values, so your key will be the same for each childs, that's why you need the commentsArray index.

How to use conditional to generate element on the page

for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX

Writing an If condition at React

I try to write an if statement inside the loop but it gives an error. I tried to use {} but it does not work too, how can I write it properly.
`<Option
optionText={option}
colorText = {props.colors[index]}
priceText = {props.prices[index]}
rangeText = {props.ranges[index]}
domainText = {props.domains[index]}
if(this.props.colorText == this.props.domainText){
//I want to write a if condition here but it says Identifier expected
}
count= {index + 1}
handleDeleteOption={props.handleDeleteOption}
/>
</div>
))
}
</div>`
import React from 'react';
const Option = (props) => (
<p className="add-option-color">
props.rangeText = {(props.colorText == props.domainText) ? props.rangeText :
props.domainText}
{props.count}. Product: {props.optionText} , Color: {props.rangeText} , Price: {props.priceText}</p>
<button className="button button--link" onClick={(e) => {
props.handleDeleteOption(props.optionText,props.colorText,props.priceText);
}}
>
remove
</button>
<hr></hr>
</div>
);
export default Option;
The clean way is yo use the ternary operator like in this example :
<Option
optionText={option}
colorText = {props.colors[index]}
priceText = {props.prices[index]}
rangeText = {props.ranges[index]}
domainText = {props.domains[index]}
YOURPROPS = {(this.props.colorText == this.props.domainText) ? VALUEIFTRUE :
VALUEIFFALSE}
count= {index + 1}
handleDeleteOption={props.handleDeleteOption}
/>
you can call a function like as following --->
getAllParams() {
if(this.props.colorText == this.props.domainText){
return "pass what you want pass"
} else {
return
}
}
render() {
<Option
optionText={option}
colorText = {props.colors[index]}
priceText = {props.prices[index]}
rangeText = {props.ranges[index]}
domainText = {props.domains[index]}
{...this.getParams()}
count= {index + 1}
handleDeleteOption={props.handleDeleteOption}
/>
}

Iterate in multiple states while rendering

I'm trying to render a component that takes data from a local API and it's been set it into multiple states, at the moment I'm mapping through one state and I need to use some data from another state. How can I access this data and map through it once?
In prodsLink={'https://www.sodimac.cl/sodimac-homy/product/' + skuData.productId + '/'}
I need to render something like this:
https://www.sodimac.cl/sodimac-homy/product/productID/productLocation
but if I do this:
prodsLink={'https://www.sodimac.cl/sodimac-homy/product/' + skuData.productId + '/' + this.state.ids.map(skuMarca => skuMarca.marca)}
It will render something like this:
https://www.sodimac.cl/sodimac-homy/product/productID/productLocation01,productLocation02,productLocation03,productLocation04
UPDATE:
This is the output I'm looking for (once per loop not all of them in one array):
BUTTON 01
BUTTON 02
BUTTON 03
BUTTON 04
I know is confusing so here is my code.
render() {
return (
this.getWebServiceResponse(this.state.ids.map(e => e.sku).join('-'), 96),
this.state.active == true &&
<div className="row" style={{ width: '89%', margin: '0 auto' }}>
<div className="blockCatPriceSlide">
{this.state.products.map(skuData =>
window.innerWidth > 440 && skuData.status === 'OK' ?
<ContenidoUno
prodsName={skuData.name.substr(0, 30) + '...'}
prodsId={skuData.productId}
prodsStatus={skuData.status}
prodsPublished={skuData.published}
prodsNormal={skuData.NORMAL.toLocaleString().replace(',', '.')}
prodsCMR={skuData.CMR}
prodsCombo={skuData.combo}
prodsAhorro={skuData.savings}
prodsStock={skuData.stockLevel}
prodsAntes={skuData.NORMAL + skuData.savings > skuData.NORMAL ? <small> Antes: $ {skuData.NORMAL + skuData.savings} </small> : ''.toLocaleString().replace(',', '.')}
prodsLink={'https://www.sodimac.cl/sodimac-homy/product/' + skuData.productId + '/'}
prodsImg={'https://picsum.photos/g/570/250'}
prodsIcon={(skuData.combo === true &&
<img src='https://via.placeholder.com/100x50/f41435/ffffff?text=combo' className="iconic" alt="producto" />) ||
(skuData.CMR !== undefined && <img src='https://via.placeholder.com/100x50/f41435/ffffff?text=CMR' className="iconic" alt="producto" />)}
catName={skuData.webCategoryName}
/> :
<ContenidoUno
prodsName={'Producto sin información...'}
prodsId=''
prodsStatus=''
prodsPublished=''
prodsNormal=''
prodsCMR=''
prodsCombo=''
prodsAhorro=''
prodsStock=''
prodsAntes=''
prodsLink=''
prodsImg={'https://picsum.photos/g/570/250'}
prodsIcon=''
catName=''
/>
)
}
</div>
</div>
)
}
}
Array.prototype.map returns a new array, so in this situation you're just returning array of [marca?, marca?, ...], so instead of returning, marca? from map you should returning whole link like so:
prodsLink={ this.state.ids.map(({ marca }) =>
`https://www.sodimac.cl/sodimac-homy/product/${skuData.productId}/${marca}`
)}
this will generate link array:
[
https://www.sodimac.cl/sodimac-homy/product/${skuData.productId}/${marca},
https://www.sodimac.cl/sodimac-homy/product/${skuData.productId}/${marca},
https://www.sodimac.cl/sodimac-homy/product/${skuData.productId}/${marca},
...
]

ReactJS: create DOM on the fly

How to transform this:
{dataFormat: 'hello my [friend=https://en.wikipedia.org/wiki/Friendship]'}
to this:
<div>
hello my <a onClick={...} href="https://en.wikipedia.org/wiki/Friendship">friend</a>
</div>
I need to somehow be able to scan a string and create links on the fly. Any idea?
The dataFormat can contain more than one link with unknown order between "regular" text and links.
Ended up using regex which did the job.
JSBin: https://jsbin.com/yogepa/edit?js,output
Code:
renderSpan(content) {
return <span>
{content}
</span>
}
renderLink(content) {
const parts = content
.replace(/\[|\]/g, '')
.split('=');
return <a style={ styles.link } onClick={ alert }>
{parts[0]}
</a>
}
renderFormat() {
let { dataFormat } = this.state;
const regex = /(\[[^\]]+])*([^\[]+)(\[[^\]]+])*(\[[^\]]+])*([^\[]+)(\[[^\]]+])*(\[[^\]]+])*([^\[]+)(\[[^\]]+])*/;
const matches = regex.exec(dataFormat);
return matches.reduce((output, match, index) => {
if (match && index >= 2) {
output.push(match.indexOf('[') >= 0 ?
this.renderLink(match) :
this.renderSpan(match)
);
}
return output;
}, []);
}
I probably can improve the Regex expression though.

Resources