I have written a small piece of code inside the return. for example
const Demo = (props) => {
return (
<div>
props.map((val, index) => (
<h2>{val.fileName}</h2>
))
</div>
)
}
The output is coming like this:
F:\test\form\student.html
But inside I don't want this type of output. I want to modify the output like: F:\test\form\form.pdf
The last student.html will remove and the form must become 2 times will repeat and lastly, the extension is pdf
original output: F:\test\form\student.html
desired output: F:\test\form\form.pdf
can you help how to solve this problem?
Hope this will help as per my understanding of your question
const Demo = (props) => {
const processedFileNames = props.map((val, index) = {
const splitFileName = val.fileName.split('\\');
splitFileName[splitFileName.length - 1] = splitFileName[splitFileName.length - 2];
return splitFileName.join('\\') + '.pdf';
})
return (
<div>
processedFileNames.map((val, index) => (
<h2>{val}</h2>
))
</div>
)
}
Please let me know if you need any other help.
Related
I'm making a blackjack hand simulation and I've encountered an issue with my code.
The game goes like this: users gets two random cards and a total of the points, clicks 'hit' to get another random card from the deck. Now that's all working but there's one more rule: if that card is an Ace, user chooses if they want to get 1 or 10 points. I implemented it before when I only had one card at a time with useEffect, however now I refactored my code and the total isn't kept in useState + the array has two cards that need to evaluated, not the most recent one.
I've tried putting my loop and if statement in a useEffect and conditionally render the Popup to let the user decide (with and without dependencies), but when I put the useState() to trigger the condition, it throws an error that there have been too many renders and I'm not sure why that is.
Here's my Home component:
import {useState, useEffect} from 'react'
import Card from '../components/Card';
import Total from '../components/Total';
import Popup from '../components/Popup'
import {shuffle} from '../hooks/shuffleCards'
import {deckArray} from '../utils/data'
export default function Home(){
const startHandSize = 2
const [starterDeck, setStarterDeck] = useState(shuffle(deckArray))
const [howManyDealt, setHowManyDealt] = useState(startHandSize)
const [triggerPopup, setButtonPopup] = useState(false)
const deal = () => {
setHowManyDealt(startHandSize)
setStarterDeck(shuffle(deckArray))
}
const hit = () => !bust && setHowManyDealt(prev => prev + 1)
const usersCards = starterDeck.slice(-howManyDealt)
var total = 0
usersCards.forEach(function (arrayItem) {
if(arrayItem.card === "A"){
alert("you have an ace")
}
else{
total += arrayItem.value
}
});
const bust = total > 21;
return(
<div>
<button onClick={deal}>DEAL</button>
<button disabled={bust} onClick={hit}>HIT</button>
<button disabled={bust}>STAND</button>
<Total total={total} usersCards={usersCards}/>
<Card usersCards={usersCards}/>
{triggerPopup && <Popup total={total} setButtonPopup={setButtonPopup}/>}
</div>
)
}
and my Popup:
export default function Popup({total, setButtonPopup}){
const handleClick = (points) => {
total += points
setButtonPopup(false)
}
return(
<div className="popup">
<div className="popup-inner">
<h4>You've got an Ace. Would you like to collect 1 or 10 points?</h4>
<button className=".btn-popup" onClick={() => handleClick(1)}>1 POINT</button>
<button className=".btn-popup" onClick={() => handleClick(10)}>10 POINTS</button>
</div>
</div>
)
}
Any help much appreciated!
Good attempt. However, there seems to be a general misunderstanding about state. Consider this code:
const handleClick = (points) => {
total += points
setButtonPopup(false)
}
total is a purely local variable to Popup, so this += pretty much does nothing. To change state in the caller, you'd normally pass a callback that can trigger a setState and move the new value for total into state.
Remember: any data change must happen immutably, and if you want to trigger a re-render, you have to set state. Of course, there are ways to circumvent this flow using refs and so forth, but these are escape hatches you shouldn't use if you don't have to.
However, a design with total kept in state strikes me as redundant. We already know the total based on the cards in play. A better strategy seems to be having ace values individually settable via the popup modal, assuming you don't want to auto-compute these ace values to be as high as possible without busting or use a toggle switch instead of a modal.
I kept going with my code from your previous question and added the modal. I'm treating high aces as 11 per the rules of Blackjack, but you can easily make that 10 if you want.
As before, I'm hoping you can apply the techniques here to your code. The keys are the handleAceSet callback and the new piece of state aceToSet, which is a ace the user has picked, or null if the user hasn't chosen an ace. aceToSet is like your setButtonPopup, but tracks an object or null rather than a boolean. When aceToSet isn't null, the user has selected an ace and we show the modal to let them pick a value for it.
handleAceSet may seem a bit complex, but it has to be due to immutability. It finds the index of the ace the user wants to set in the deck array, then creates a new object at this index with the new value and glues the subarray slices before and after the index back together.
// utility library "import"
const cards = (() => {
const shuffle = a => {
a = a.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = ~~(Math.random() * (i + 1));
const x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
};
const frz = (...args) => Object.freeze(...args);
const suits = frz([..."HCSD"]);
const faces = frz([..."AJQK"]);
const pips = frz([...Array(9)].map((_, i) => i + 2));
const ranks = frz([...pips, ...faces]);
const cards = frz(
suits.flatMap(s =>
ranks.map(r =>
frz({
rank: r,
suit: s,
str: r + s,
value: isNaN(r) ? (r === "A" ? 1 : 10) : r,
})
)
)
);
const shuffled = () => shuffle(cards);
return {shuffled};
})();
const {Fragment, useState} = React;
const AceSetterModal = ({handleSetLow, handleSetHigh}) => (
<div>
<button onClick={handleSetLow}>Set ace low</button>
<button onClick={handleSetHigh}>Set ace high</button>
</div>
);
const Card = ({card, handleAceSet}) => (
<div>
{card.str}
{card.rank === "A" && (
<Fragment>
{" "}
<button onClick={handleAceSet}>
Set ({card.value})
</button>
</Fragment>
)}
</div>
);
const Game = () => {
const startHandSize = 2;
const goal = 21;
const lowAce = 1;
const highAce = 11;
const [deck, setDeck] = useState(cards.shuffled());
const [cardsDealt, setCardsDealt] = useState(startHandSize);
const [aceToSet, setAceToSet] = useState(null);
const handleAceSet = value => {
setDeck(deck => {
const i = deck.findIndex(e => e.str === aceToSet.str);
return [
...deck.slice(0, i),
{...aceToSet, value},
...deck.slice(i + 1),
];
});
setAceToSet(null);
};
const deal = () => {
setCardsDealt(startHandSize);
setDeck(cards.shuffled());
};
const hit = () => !bust && setCardsDealt(prev => prev + 1);
const cardsInPlay = deck.slice(-cardsDealt);
const total = cardsInPlay.reduce((a, e) => a + e.value, 0);
const bust = total > goal;
return (
<div>
{aceToSet ? (
<AceSetterModal
handleSetLow={() => handleAceSet(lowAce)}
handleSetHigh={() => handleAceSet(highAce)}
/>
) : (
<Fragment>
<button onClick={deal}>Deal</button>
<button disabled={bust} onClick={hit}>
Hit
</button>
<div>
{cardsInPlay.map(e => (
<Card
key={e.str}
handleAceSet={() => setAceToSet(e)}
card={e}
/>
))}
</div>
<div>Total: {total}</div>
<div>{bust && "Bust!"}</div>
</Fragment>
)}
</div>
);
};
ReactDOM.createRoot(document.querySelector("#app"))
.render(<Game />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="app"></div>
I am wanting to take a string and get the JSX to replace all the words in between brackets to be bold. I got it to work with this, but just wondering if there is a better way of going about this?
const jsxArray = [];
let unformattedString = "[name] Hi there, my name is [name], I am [age] years old, and I work in the field of [profession]";
const boldStrings = unformattedString.match(/(?<=\[).+?(?=\])/g);
const notBoldStrings = unformattedString.split(/\[.+?\]/g);
let j = 0;
if (boldStrings !== null) {
notBoldStrings.forEach((notBoldString, index) => {
if (index === 0 && notBoldStrings === "") {
// the first word should be bolded
jsxArray.push(<b>{boldStrings[j]}</b>);
} else {
jsxArray.push(notBoldString);
jsxArray.push(<b>{boldStrings[j]}</b>);
}
j++;
});
} else {
jsxArray.push(notBoldStrings[0]);
}
The expected output would be:
name Hi there, my name is name, I am age years old, and I work in the field of profession
You can use this code:
export default function App() {
let unformattedString =
"[name] Hi there, my name is [name], I am [age] years old, and I work in the field of [profession]";
const boldString = (str, substr) =>{
str.replaceAll(substr, `<b>${substr}</b>`);
}
const strArr = unformattedString.split(" ");
const formattedString = strArr
.map((item) => {
let strBold = item.match(/(?<=\[).+?(?=\])/g);
if (strBold) {
return boldString(item, strBold);
} else {
return item;
}
})
.join(" ");
return (
<div className="App">
<div dangerouslySetInnerHTML={{ __html: formattedString }} />
</div>
);
}
To see its result: codesandbox.io
But about dangerouslySetInnerHTML, Setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack. And recommended using the html-react-parser package in your React project.
npm install html-react-parser
To use:
import parse from 'html-react-parser'
const yourHtmlString = '<h1>Hello</h1>'
and in return part:
<div>
{parse(yourHtmlString)}
</div>
So basicly what I want is,
I am passing one array in List component like this :
<List items={["A", "B", "C"]} />
Now I need to print each element in list like this :
● A
● B
● C
But whenever someone will click any list element, that element should come at the 1st place.
Example : if I clicked B, then output should look like this :
● B
● A
● C
Any solution for this, please help.
you can do something like this
const List = ({items}) => {
const [list, setList] = useState(items)
return <ul>{list.map((l, i) =>
<li
onClick={() => setList([l, ...items.filter(item => item !== l)])}
key={i}>
{l}
</li>)}
</ul>
}
Create a copy of the value you want to move.
Create a copy of your array without the value using filter. or splice if you have the index.
Add the value to the Array copy using unshift
Set your state to the Array copy
Doc :
https://love2dev.com/blog/javascript-remove-from-array/
https://www.w3schools.com/jsref/jsref_unshift.asp#:~:text=The%20unshift()%20method%20adds,method%20overwrites%20the%20original%20array.
const Home = () => {
const [myList, setMyList] = useState(["A","B","C"]);
function shiftPlace(from, to) {
const arr = myList;
let cutOut = arr.splice(from, 1)[0];
arr.splice(to, 0, cutOut);
setMyList([...arr]);
}
return (<div>
{
myList.map((v, i) => {
return <span key={i} onClick={() => {
shiftPlace(i, 0);
}}>{v}</span>;
})
}
</div>);
};
This does everything you need.
I have a component with a method to add/create a new sub-component and insert it into an array at a specific index (i.e. [elem1, elem2, elem3] becomes [elem1, elem2, new elem, elem3]).
When using something like array.splice(currentIndex + 1, 0, newElement), in the DOM the last element that already exists is just duplicated, though it appears to update correctly for the array in state.
The only way I have found that works is to just use array.push(newElement), which does render properly but is not the desired effect. I've tried a few more ways of inserting the new element into the array but so far everything other than .push() causes the duplication issue. Any suggestions or tips would be greatly appreciated!
const Notef = ({ fetchedParts }) => {
const [parts, setParts] = useState(fetchedParts);
const addNewPart = (currentPart) => {
const index = parts.map((p) => p.id).indexOf(currentPart.id);
const updatedParts = [...parts];
const newPart = { id: pid(), html: 'Type here...', tag: 'p' }
// This will cause duplicated element
updatedParts.splice(index + 1, 0, newPart);
// This does not create duplicate
//updatedParts.push(newPart);
setParts(updatedParts);
}
return (
<>
{parts.map((part) => {
const position = parts.map((p) => p.id).indexOf(part.id) + 1;
return (
<Part
key={part.id}
position={position}
id={part.id}
tag={part.tag}
html={part.html}
addPart={addNewPart}
/>
);
})}
</>
);
}
export default Notef;
Element duplication w/ .splice()
Element addition w/ .push()
edit: re-worded intro to specify new element to be inserted rather than replaced.
Dominic found the issue - I was using indices for keys in the rendered component. Replaced that with the ID and it solved the issue!
return (
<>
{parts.map((part) => {
const position = parts.map((p) => p.id).indexOf(part.id) + 1;
return (
<Part
key={part.id}
position={position}
id={part.id}
tag={part.tag}
html={part.html}
addPart={addNewPart}
/>
);
})}
</>
);
I do apologize in advance I'm a JS/ReactJS newbie and I have had trouble phrasing the question, so if this is already answered somewhere I am sorry.
I have an object like this:
Object {Joy: 0.719115, Extraversion: 0.59527, Agreeableness: 0.650457}
I'd like to be able to return html for all of the keys in the object. As of right now it only returns the html for the first key (obviously, as return breaks the loop, if I'm not mistaken). How do I achieve rendering of html of all the keys from the object?
import React from 'react'
export const MessageSentiment = (props) => {
var sentiment = JSON.parse(props.sentiment)
console.log(sentiment)
for(var key in sentiment ) {
console.log(key, sentiment[key])
return (<h1>{key}: {sentiment[key]*100}</h1>)
}
}
These are the output and required output
Output:
<h1>Joy: 71.9115</h1>
Expected output:
<h1>Joy: 71.9115</h1>
<h1>Extraversion: 59.527</h1>
<h1>Agreeableness: 65.0456</h1>
Not sure if this has anything to do with it, but I get a warning in the console:
../src/components/messages/MessageSentiment.js
6:5 warning The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype guard-for-in
✖ 1 problem (0 errors, 1 warning)
printWarnings # webpackHotDevClient.js:196
handleWarnings # webpackHotDevClient.js:209
connection.onmessage # webpackHotDevClient.js:255
EventTarget.dispatchEvent # eventtarget.js:49
(anonymous) # main.js:274
SockJS._transportMessage # main.js:272
EventEmitter.emit # emitter.js:44
WebSocketTransport.ws.onmessage # websocket.js:35
Two things here.
You need to always return one single element, in this case a div. Inside of this element you can have whatever you want, but it has to be a single parent component.
You will use map to iterate an array and get a new array. In this case the new array will contain the <h1/> elements.
```
export const MessageSentiment = (props) => {
const sentiments = JSON.parse(props.sentiment);
const keys = Object.keys(sentiments);
return (
<div>
{ keys.map(key => (<h1 key={key}>{key}: {sentiments[key]*100}</h1>)) }
</div>
);
}
```
Regards
A React component can't return multiple React elements. You should wrap them inside <div> or any other container element.
export const MessageSentiment = (props) => {
var sentiment = JSON.parse(props.sentiment)
return (
<div>
{
Object.keys(sentiment).map(key => (
<h1 key={key}>{key}: {sentiment[key]*100}</h1>
))
}
</div>
)
}
And remember: keys should be given to the elements inside the array to give the elements a stable identity.
you need to collect all the HTML in the array and return it. you can do 2 way
using map - map return new array without modifying existing array
.
export const MessageSentiment = (props) => {
var sentiment = JSON.parse(props.sentiment)
return (
<div>
{
Object.keys(sentiment).map((key, index) => <h1 key={index}> {key}:{sentiment[key]*100}</h1>)
}
</div>
)
}
Using array push method
.
export const MessageSentiment = (props) => {
var sentiment = JSON.parse(props.sentiment)
let itemList = [];
for(var key in sentiment ) {
itemList.push(<h1>{key}: {sentiment[key]*100}</h1>)
}
return (
<div>{itemList}</div>
)
}