reusing array.map() in React.js - reactjs

I can't find an answer in "many similar questions", sorry I'm new to react.
I have the following code inside return of my sfc that works/renders fine:
{data && allToDos && completed === 'all' && data.map(data => {
key++;
return (
<div key={key}>
<h2>{data.title}</h2>
<p className="apart">
Nr: {data.id} <span>Status: {!data.completed && <span>Not </span>}completed</span>
</p>
<h3>User: {data.userId}</h3>
<br/><hr/><br/>
</div>
);
});}
Now, I have 3 different options for completed and in each case i want to render different set of data, so i have isolated the data.map(data => {/*code*/}) into a separate function inside the same component,like that:
const dataMap = () => {
data.map(data => {
key++;
return (
<div key={key}>
<h2>{data.title}</h2>
<p className="apart">
Nr: {data.id} <span>Status: {!data.completed && <span>Not </span>}completed</span>
</p>
<h3>User: {data.userId}</h3>
<br/><hr/><br/>
</div>
);
});
}
and then i passed that function inside return like that:
{data && allToDos && completed === 'all' && dataMap()}
and nothing is rendered, but no error messages either.

You're not returning from dataMap() function it returns undefined .
try this
const dataMap = () => {
return data.map(data => {
key++;
return (
<div key={key}>
<h2>{data.title}</h2>
<p className="apart">
Nr: {data.id} <span>Status: {!data.completed && <span>Not </span>}completed</span>
</p>
<h3>User: {data.userId}</h3>
<br/><hr/><br/>
</div>
);
});
}

Related

How to disable a button within a mapped div for a period of time

I have an array of objects, and by .map() method i am returning the objects' elements within the divs. There is this button that calls a function with 24hr cooldown, and i would like to disable the button for that period of time when it's clicked. Otherwise it throws me an error from the serverside (I'm using blockchain as the backend, and transactions throw error if the requirements aren't met). I can't use a state like isButtonDisabled, because i just want a specific button to be disabled.
Here is the mapped div part of the code;
<div className="row" id="chars-row">
<main role="main" className="col-lg-12 d-flex flex-wrap justify-content-center">
{ characters.map( (chr, key) => {
return(
<div className="col-md-2 card m-1" key={key} >
<img className="card-img-top chr_img" src= {(Math.max(chr.strength, chr.health, chr.dexterity, chr.intelligence, chr.magic) == chr.strength)
? require('../assets/str.png')
: (Math.max(chr.strength, chr.health, chr.dexterity, chr.intelligence, chr.magic) == chr.health)
? require('../assets/hp.png')
: (Math.max(chr.strength, chr.health, chr.dexterity, chr.intelligence, chr.magic) == chr.dexterity)
? require('../assets/dex.png')
: (Math.max(chr.strength, chr.health, chr.dexterity, chr.intelligence, chr.magic) == chr.intelligence)
? require('../assets/int.png')
: (Math.max(chr.strength, chr.health, chr.dexterity, chr.intelligence, chr.magic) == chr.magic)
? require('../assets/mgc.png') : null } alt=""/>
<div className="card-body">
<div> <b>Name:</b> <span>{(chr.name).replace(/['"]+/g, '')}</span></div>
<div> <b>ID:</b> <span>{key}</span></div>
<div> <b>STR:</b> <span>{chr.strength}</span></div>
<div> <b>HP:</b> <span >{chr.health}</span></div>
<div> <b>Dex:</b> <span>{chr.dexterity}</span></div>
<div> <b>INT:</b> <span >{chr.intelligence}</span></div>
<div> <b>MAGIC:</b> <span >{chr.magic}</span></div>
<div> <b>LEVEL:</b> <span >{chr.level}</span></div>
<div> <b>EXP:</b> <span >{chr.experience}</span> / 255</div>
<div className="progress">
<div className="progress-bar progress-bar-striped bg-info" role="progressbar" style={{'width': (chr.experience * 100 / 255) + '%'}} aria-valuenow="0" aria-valuemin="0" aria-valuemax="255"></div>
</div>
<small className="text-secondary owner_address">{ownerAccounts[key]}</small>
</div>
<button className="btn btn-danger my-1" value={key} onClick={openModal(key)}>Battle!</button>
</div>
)
})}
</main>
</div>
These are just snippets. But the idea is working. This will make the clicked button disabled.
Parent
const Parent = () => {
const data = ['a', 'b', 'c'];
return (
<div>
{data.map((data) => (
<Child/>
))
}
</div>
)
}
Child
const Child = ({data}) => {
const [disable, setDisable] = useState(false);
return (
<div onPress={ () => setDisable(true)}>
{data}
</div>
)
}
export default Child
First of all, thanks for the helps. I have solved my problem.
I could query my last battle epoch time for each member of the array from blockchain
(from mapping(uint256 => uint256) public tokenIdToCooldown ):
useEffect(() => {
const getLastBattle = async () => {
let temp = [];
let totalChars = await contract.methods.getTotalCharacterNumber().call({ from: accounts });
for(let i=0; i<totalChars; i++) {
let lastBattle = await contract.methods.tokenIdToCooldown(i).call();
temp[i] = lastBattle;
}
setLastBattle(temp);
}
if(typeof web3 !== 'undefined' && typeof accounts !== 'undefined' && typeof contract !== 'undefined') {
getLastBattle();
}
}, [accounts, web3, contract])
Then i have checked whether that time passes now (after adding 24hr in seconds), and assinged a bool value for each of them;
useEffect(() => {
const lastBattleBool = async () => {
var now = Math.round(Date.now() / 1000);
let totalChars = await contract.methods.getTotalCharacterNumber().call({ from: accounts });
let temp = [];
for(let i=0; i<totalChars; i++) {
if((lastBattle[i] + 86400) < now) {
temp[i] = true;
} else {
temp[i] = false;
}
setBoolBattle(temp);
}
}
if(typeof web3 !== 'undefined' && typeof accounts !== 'undefined' && typeof contract !== 'undefined') {
lastBattleBool();
}
}, [lastBattle, accounts, web3, contract]);
My last approach was checking that bool value before displaying the button;
{boolBattle[characterCountOfOwner[key]]
? <button className="btn btn-danger my-1" value={characterCountOfOwner[key]}
onClick={battle(characterCountOfOwner[key])}>Battle!</button>
: null}

while using map() function props.data is undefined

its shows data without map function in console
but whenever I use map function it shows props.data is undifned and also undifined in console
I have used the same code for another page and that works
const Test_Footer = (props) => {
console.log("ok", props.data)
const newvar =props.data.map((item) => {
return (
<>
<li>{item.data.id}</li>
</>
)
})
// console.log(newvar)
return (
<div>
<div class="main-content">
<footer className="footer">
<div className="review-checkbox"><input type="checkbox" /><label>Review</label></div>
<div className="question-nav">
<ul className="pagination">
{newvar}
</ul>
<button className="minimize-btn ml-10"><img src="images/minimize-btn.png" /></button>
</div>
</footer>
</div>
</div >
)
}
export default Test_Footer
const newvar = props && props.data && props.data.map((item) => {
return (
<>
<li>{item.data.id}</li>
</>
)
})

React child not re-rendered when parents props change

I'm having some issues with child re-rendering, I pass methods to children to see if a button should be displayed or not but when the state of the parent changes, the children are not re-rendered.
I tried with the disabled attribute for the button but didn't work either.
Here's my code (I removed unnecessary part):
function Cards(props) {
const isCardInDeck = (translationKey) => {
return props.deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
props.deck.push(card);
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
var index = props.deck.findIndex(
(c) => c.translationKey === card.translationKey
);
props.deck.splice(index, 1);
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}
function Card(props) {
const toggleShowDescription = () => {
if (!showDescription) {
setShowDescription(!showDescription);
}
};
return (
<div onClick={toggleShowDescription} onBlur={toggleShowDescription} >
<img src={"../images/cards/" + props.card.image} alt={props.card.image + " not found"} />
{showDescription ? (
<div className="customCardDetail">
<div className="cardName"></div>
<div className="cardType">
{props.addToDeckDisabled ? (
<Button onClick={() => { props.removeFromDeckClick(props.card);}} startIcon={<RemoveIcon />}>
Remove from deck
</Button>
) : (
<Button onClick={() => { props.addToDeckClick(props.card); }} startIcon={<AddIcon />}>
Add to deck
</Button>
)}
</div>
<div className="cardDescription">
<span>
<FormattedMessage id={props.card.description} defaultMessage={props.card.description} />
</span>
</div>
</div>
) : (
""
)}
</div>
);
}
You code does not update state. Cards mutates the props that it is receiving.
To use state in a functional component in React you should use the useState hook.
Cards would then look something like this:
function Cards(props) {
const [deck, setDeck] = useState(props.initialDeck)
const isCardInDeck = (translationKey) => {
return deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
setDeck([...deck, card])
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
setDeck(deck.filter(deckItem => deckItem.translationKey !== card.translationKey))
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}

How to Highlight the search keyword in the autosuggestion result list React JS

I have a search term, and based on the search term corresponding results are displayed. Trying to highlight the keyword which is search in the results list. But its giving me empty value.
this is what I tried:
render() {
const { label } = this.props;
const { searchResults, query } = this.state;
let textHighlighter;
return (
<div>
<form>
<input placeholder={label} ref={input => (this.search = input)} onChange={e => this.handleInputChange(e)} />
</form>
{searchResults &&
searchResults.results &&
searchResults.results.length > 0 && (
<ul>
{searchResults.results.map((result, i) => {
let searchKeywordIdx = result.indexOf(query);
if (searchKeywordIdx > -1) {
textHighlighter = [
result.substring(0, searchKeywordIdx),
<strong key={i}>
{result.substring(searchKeywordIdx, searchKeywordIdx + query.length).substring(searchKeywordIdx + query.length)}
</strong>
];
}
return <li key={result}>{textHighlighter}</li>;
})}
</ul>
)}
</div>
);
}
Note: this.state.query = "aaa"
Example JSON:
searchResults.results = [“aaa”, “aaa bbb”, “aaa ccc”]
So I need to highlight all the "aaa" in the results list.
https://codepen.io/smilesaayush/pen/NzgRmW?editors=0010
fixed a little problem in your textHighlighter.
return (
<div>
{searchResults &&
searchResults.results &&
searchResults.results.length > 0 && (
<ul>
{searchResults.results.map((result, i) => {
let searchKeywordIdx = result.indexOf(query);
if (searchKeywordIdx > -1) {
textHighlighter = [
result.substring(0, searchKeywordIdx),
<strong key={i}>
{result.substring(searchKeywordIdx, searchKeywordIdx + query.length)}
</strong>,
result.substring(searchKeywordIdx + query.length)
];
}
return <li key={result}>{textHighlighter}</li>;
})}
</ul>
)}
</div>
);

How do you toggle filtered list items inside of a div in React?

I'm building a React component that shows a filtered list of items in a div when users click on a button. Only the items within that div should be displayed on click. For some reason, though, the lists for every section are being toggled.
What am I doing wrong?
Here is my code: https://codesandbox.io/s/6yr0jzlpwn
Simply you can just define a specific value for each button then pass it to state
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords === '1' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords === '2' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "茶";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords === '3' && (
<ul>
{this.state.list.filter(function(word) {
return word[0] === "日";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
In toggleWords function
toggleWords(e) {
const clickedButton = e.target.value;
if(clickedButton !== this.state.showWords){
this.setState({ showWords: clickedButton })
}else{
this.setState({ showWords: '' }) // handle close list if double click
}
}
In case if you want to expand two sections at once you need to change showWords state to be an array then use indexOf method to extend the section
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords.indexOf('1') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords.indexOf('2') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "茶";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords.indexOf('3') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[0] === "日";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
Then in toggleWords function will delete the value from array if exist else it will add it
toggleWords(e) {
const clickedButton = e.target.value;
if (this.state.showWords.indexOf(clickedButton) !== -1) { // deleting the value from array if exist
this.setState(prevState => ({ showWords: this.state.showWords.filter(d => d !== clickedButton) }))
} else {
this.setState(prevState => ({ showWords: [...prevState.showWords, clickedButton] }))
}
}

Resources