use useState to append string values - javascript

Hi I want to create a global variable in react but I would like to avoid it, so I created a component and want to store string data in it such as below:
export default function settings() {
const [stringData, setStringData] = useState("");
I want this state to concactnate input string. For example
setStringData("I am ");
setStringData(" a ");
setStringData("robot.");
When I retrieve stringData I want it to be printed as
I am
a
robot.
How do I do this ?

When you calling setStringData it is the same as though you just assign a value to a variable, e.g. stingData = 'asdf' so if you need to concatenate strings you need to add the current value and the string you want to add, below you can find an example.
const [stringData, setStringData] = useState("");
setStringData(stringData + "Some Sting To Append!");
UPD: Based on the comments, showing an example with some listener.
const [stringData, setStringData] = useState("");
const addString = () => setStringData(stringData + " Clicked!");
return (<button onClick={addString}> Add Text </button>);

Related

JS React: replacing regex with component in a string

Given a string with the format:
'This is a string with some ::FaIconUpload:: icons in it as ::FaIconDownload:: these two.'
I'd like to split it using a RegEx and replace the coincidences with some React components. The string would have the type of component (FaIcon) and a props in it such as the name of the icon (Upload).
The objective with this is to be able to use React components within translated strings, and the expected return value would be something like:
[
'This is a string with some ',
<FaIcon iconName="Upload" />,
' in it as ',
<FaIcon iconName="Download" />,
' these two.'
]
The method
Currently, I've got a method which returns either a string or an array. This is compatible with React render methods, since if we return an array it will be capable of rendering any components on that array.
As I'll use it to translate some strings, I've created this custom hook:
const useCustomTranslation = () => {
const { t } = useTranslation();
const tlt = (...args) => {
// const str = t(...args);
const testStr =
'This is a string with some ::FaIconUpload:: icons in it as ::FaIconDownload:: these two.';
const reg = /(?<=::FaIcon)(.*?)(?=::)/g;
const preStrAr = testStr.split(reg);
console.log(preStrAr);
};
return { tlt };
};
The problem
Currently, this method is logging this:
[
"This is a string with some ::FaIcon",
"Upload",
":: icons in it as ::FaIcon",
"Download",
":: these two."
]
As you can see, it's not including the ::FaIcon and the final ::, as I haven't been able to find the right Regex to do so. But even if I got to that point, I feel like then I should have to re-iterate through the array to replace the strings with the right component, again using Regex to see if the array item matches the right format.
I find this somehow overcomplicated, and I think there has to be a much cleaner and easy way to get it (correct me if maybe I'm wrong and this is the only way).
Is there any way where I can split a string using a Regex, using part of the matched group to replace the string by another content using that matched string?
Perhaps you meant to do this?
/::FaIcon(.*?)::/ without the look
const str = `This is a string with some ::FaIconUpload:: icons in it as ::FaIconDownload:: these two.`
const newText = str.replace(/::FaIcon(.*?)::/g,function(_,match) {
return `<FaIcon iconName="${match}" />`
})
console.log(newText)
To make an array you can do
const str = `This is a string with some ::FaIconUpload:: icons in it as ::FaIconDownload:: these two.`
const newText = str.replace(/\s?::FaIcon(.*?)::\s?/g,function(_,match) {
return `::<FaIcon iconName="${match}" />::`
}).split("::")
console.log(newText)
Finally, I've made it using (sadly) the re-iteration method, as it's the only way I can see it would work. Thanks to #mplungjan for his first answer, which gave me the hints to get it working:
export const replaceIconInStr = (str) => {
// Matches the whole icon component pattern
const regComponent = /(::FaIcon.*?::)/g;
// Matches just the component prop we need
const regIconName = /::FaIcon(.*?)::/g;
// Split the string by the component pattern
const splittedStr = str.split(regComponent);
// If there are any matches
if (splittedStr.length) {
// Match the elements in the array and get the prop to replace it by the real component
return splittedStr.map((el) => {
const matched = regIconName.exec(el)?.[1];
if (matched) {
return <FaIcon iconName={matched} />;
}
return el;
});
}
// If there is no pattern matching, return the original string
return str;
};

UseState how to set an Array back to empty?

I'm trying set clickFavIconArray back to an empty array with the hook.
Basically, the setClickFavIconArray has a list of IDs the showFavIcon() checks that ID and if it contains the same ID I want to remove it from the array and update the setClickFavIconArray to the new Array.
However, it just seems to be adding on to the original clickFavIconArray no matter what. Is there a way to clear the clickFavIconArray state back to an [] empty array?
Some help here would be awesome.
const [clickFavIconArray, setClickFavIconArray] = useState([]);
function showFavIcon(id){
if (clickFavIconArray.includes(id)) {
const newArray = clickFavIconArray.filter(item => !id.includes(item))
setClickFavIconArray(newArray)
}
setClickFavIconArray([...clickFavIconArray, id])
}
Simply pass the new value of empty array to setClickFavIconArray():
setClickFavIconArray([])
To make sure that the id is not immediately added to the array again, add a return statement inside the if-statement.
const [clickFavIconArray, setClickFavIconArray] = useState([]);
function showFavIcon(id){
if (clickFavIconArray.includes(id)) {
const newArray = clickFavIconArray.filter(item => !id.includes(item));
setClickFavIconArray(newArray);
return; // make sure that the next line is not executed
}
setClickFavIconArray([...clickFavIconArray, id])
}
There are two issues with the code
filter function seems to be invalid it should be replaced with
clickFavIconArray.filter(item => id != item)
You are adding id again to the array with this
setClickFavIconArray([...clickFavIconArray, id])
If you want to remove id, there is no need for this line in your code.
However you can always set clickFavIconArray to an empty array state using this code:
setClickFavIconArray([])

React formik and select array to string Convert onSubmit

React formik and react select package are I am using . I created on form that contains user can create new and Edit also working in same form. I have an one multi select field that field values are in array I want to change to string send to server on onSubmit. And also on update I want to get the string value convert array set to select field. I try it but cant found solution .please give me any ideas It helpful for me
codeSandbox:https://codesandbox.io/s/multipleselect-formik-3eqxp?file=/src/RegisterForm.js
Thanks for help
After getting the field value change the array to string on before submit. we want bind the change string value on field. It will be working fine for me.
Code Sandbox link :https://codesandbox.io/s/multipleselect-formik-3eqxp?file=/src/RegisterForm.js:657-844
function create(fields) {
console.log(fields);
const jobs = [];
fields.job.map((ele) => jobs.push(ele.value));
fields.job = jobs.toString();
console.log(fields);
}
i am not sure if this what you are looking for, but i did
function create(fields) {
fields.job = JSON.stringify(fields.job)
console.log(fields);
}
or
function create(fields) {
const jobs = [];
fields.job= fields.job.map((ele) => jobs.push(ele.value));
console.log(fields);
}
instead of
function create(fields) {
console.log(fields);
const jobs = [];
fields.job.map((ele) => jobs.push(ele.value));
const job = ("text => ", jobs.toString());
console.log(job);
console.log(fields);
}
I think you want to get the value from the array where the result should be
{name: "111", fatherName: "222", job: ["enginner", "painter"] }
function create(fields) {
const amendedFields = {
...fields,
job: fields.job.map((ele) => ele.value) // extract value from object, ["enginner", "painter"]
}; // not mutating the current fields object, copy it and mutate the new one.
console.log("old", fields);
console.log("new", amendedFields);
}

ReactJS: Page not being re-rendered on state change

I am learning react through the university of helsinki's full stack open course. The point of the code is there are two buttons and a quote shown on page. One button goes to a random quote and the other lets you put in a vote and shows how many votes that quote has total. The problem is that when I click vote, it adds the vote in the background but doesn't re-render the total amount of votes unless I change the quote.
I've tried different ways to go about conducting state change such as creating a function specifically for the setVote but I can't get it to work.
const App = () => {
const [selected, setSelected] = useState(0)
let [vote, setVote] = useState([...copyVote])
const changeAnecdote = () => {
setSelected(Math.floor(Math.random() * 6))
}
const addVote = () => {
copyVote[selected] = copyVote[selected] + 1
setVote(vote = copyVote)
}
return (
<div>
<Button onClick={changeAnecdote} text='Next Anecdote'/>
<Button onClick={addVote} text='Vote'/>
<div>
{anecdotes[selected]}
<DisplayVotes vote={vote} selected={selected}/>
</div>
</div>
)
}
copyVote is a copy of a zero-filled array and DisplayVotes simply shows how many votes total for that quote on screen.
When I check for changes in the array of votes after hitting vote through developer tools, the array doesn't change until I go to another quote.
Anyone have any ideas as to what I'm doing wrong?
There is a few things that should be updated here.
Firstly, copyVote is an array containing the number of votes for each quote. So ...copyVote will give you each item in that array. You only want the vote for the current quote. Your initial vote state should be
const [vote, setVote] = useState(copyVote[selected])
You also want to update the addVote function the way DSCH mentioned.
const addVote = () => {
// copyVote[selected]++ is the same as copyVote[selected] += 1 and copyVote[selected] = copyVote[selected] + 1
setVote(copyVote[selected]++)
}
Finally, you want to add a way to update the vote each time the anecdote is changed. You could do this in the changeAnecdote function, but a better approach would be to use an effect hook that is dependent on the selected state.
useEffect(() => {
// Set the value of vote to match the newly selected quote
setVote(copyVote[selected])
}, [selected])
Using this, the DisplayVotes vote prop is only going to display the vote for the currently selected quote. You may need to updated that component to handle this.
In setVote(vote = copyVote) the expression within the parentheses is an expression to set the vote variable with the value in copyVote. I assume that's why you use let in let [vote, setVote] = useState([...copyVote]), since probably you got an error setting a value to a const.
As setVote is what return from the useState what you probably want to do is:
const addVote = () => {
copyVote[selected] = copyVote[selected] + 1
setVote(copyVote[selected])
}

How can I map a concatenated value for this state, without it being passed as a string?

I'm new to Javascript.
I'm building this drumpad and I want to be able to switch between different soundpacks.
The sounds are imported from a separate file and the state is written like this:
import * as Sample from '../audiofiles/soundfiles'
const drumpadData = [
{
// other stuff
soundfile: Sample.sound1a
},
If I want to load a different soundpack, I have to change the state so the last letter (a,b,c) gets changed, so instead of Sample.sound1a, it would have to be Sample.sound1b. this is the function i wrote (on App.js):
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = "Sample.sound" + item.id + choice
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
It works, as in the value gets changed, but instead of react interpreting that and finding the correct import, the value of soundfile just stays as a string like "Sample.soundb1", so I get a load of media resource errors.
https://aquiab.github.io/drumpad/ heres the website, you can check the console to see the error, you have to load a different soundpack to reproduce the error.
https://github.com/aquiab/drumpad and here are the files:
I've thought of some ways of cheesing it, but I want the code to stay as clean as I can make it be.
Well that's because it is in fact a string. When you do:
"Sample.Sound" + item.id + choice you are doing type coersion. In JavaScript, that means you are converting the value of all data-types so that they share a common one. In this case your output resolves into a string. This will not be effective in finding the right sound in your dictionary.
Instead, what you need is bracket notation: Object[property]
Within the brackets we can define logic to identify the designated key belonging to the Object.
For example: Sample["sound" + item.id + choice] would evaluate to Sample["sound1b"] which is the same as Sample.sound1b
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
item.soundfile = Sample["sound" + item.id + choice]
return item
})
return {
data: updatedData
}
})
}
I can think of two approaches here.
import Sample in App.jsx. and update sound file.
import * as Sample from '../audiofiles/soundfiles'
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = Sample[`sound${item.id}${choice}`]
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
You should save mapping of files in other object and update mapping and use mapping in drumpadData soundfile key.
Your problem comes from this line :
let newSoundfile = "Sample.sound" + item.id + choice
Here you are concatening your values item.id and choice with a string, so the result is a string and Sample is not interpreted as your imported object.
What you need is to wright something like
const sampleItem = "sound" + item.id + choice
let newSoundfile = Sample[sampleItem]
When you access an object property with the notation myObject[something], what's inside the bracket get interpreted. So in my example sample (which is a string because I concatenated a string "sound" with the variables) will be replaced with its string value (ex: "sound1a"), and newSoundFile will have as value the result of Sample["sound1a"].
I hope it make sens.

Categories

Resources