I'm new to the "React" javascript framework to improve my skills, I gave myself as an exercise to convert a number or string into a symbol. For example a number will convert like this and a string will convert like this <?L> I present to you my code that I was able to write to reach the end of my exercise
import React from 'react';
import { useState } from 'react';
export default function Accueil() {
//submit
const ConvertResult = e => {
e.preventDefault();
}
//input
const [InputState, setInputState] = useState();
const InputContain = e => {
setInputState(e);
}
//button
const [ConvertInput, setConvertInput] = useState();
const ConvertData = () => {
if(typeof InputState === 'string'){
setConvertInput('?d')
}
}
return (
<div className="Accueil_Container">
<div className="Accueil_main">
<form onSubmit={e => ConvertResult(e)}>
<input type="text"
onInput={e => InputContain(e.target.value)}/> <br/>
<button onClick={ConvertData}>Convert</button>
</form>
</div>
<span>
{ConvertInput}
</span>
</div>
)
}
and here is the result I get in return
https://i.stack.imgur.com/AoRv0.png
characters such as <?d> or <?L> do not appear as many times as the string inserted in the input
if I could get some help from the more experienced or someone who has any idea how to fix this it will warm my heart.
I've been looking for a solution on the internet for 3 days, but nothing solves my problem or maybe I'm not typing the right keywords.
it seems like you set the variable "Inputstate" everytime you put in a new character but you dont give back the result every time, just once at the and.
how I roughly would put it into Code:
var Displaystring;
const InputContain = e => {
setInputState(e);
if(typeof InputState === 'string'){
Displaystring = Displaystring + '?d';
}
}
so you create a string that contains the converted data and add a symbol every time an input is registered in the input form. hope it made it a little clearer, sorry for the late answer
Related
I want to convert comma-separated values into tags. In fact I already have a good part done, but I have a problem. the tags come "added" with the previous ones. If I type in the input "ex1,ex2", the tags "ex1" and "ex1,ex2" will be created, but the tags that should be created are "ex1" and "ex2". look: .
this is my input:
<Input
onChangeText={(text: string) => handleTagChange(text)}
onKeyPress={handleTagKey}
value={inputTag}
/>
and here is the functions:
const handleTagChange = (text: string) => {setInputTag(text);}
const handleTagKey = (event: NativeSyntheticEvent<TextInputKeyPressEventData>) =>
{
const { key } = event.nativeEvent;
const trimmedInput = inputTag.trim();
if(key === ',' && trimmedInput.length && !tags.includes(trimmedInput)) {
event.preventDefault();
setTags((prev) => [...prev, trimmedInput]);
}
}
I thought it would be enough to set an empty string in the input value after setTags (setInputTag("")), but apparently it's not that simple.
would this work for you?
setTags(trimmedInput.split(","));
you must first remove the last comma
Please consider the following code: https://codepen.io/kyxey/pen/XWEWBRY
const { useRef, useState } = React;
function App() {
const inputRef = useRef(null);
const [jobs, setJobs] = useState([]);
const addJob = () => {
const newJob = inputRef.current.value;
if (newJob) {
setJobs((prevJobs) => [...prevJobs, newJob]);
}
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={addJob}>Add</button>
<ul>
{jobs.map((job) => {
const listItem = job + " " + Math.random();
return <li key={listItem}>{listItem}</li>;
})}
</ul>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
In this example, Whenever a new item gets added to the list, the entire list re-renders. You can tell this by looking at the random number in front of each item which will change with every newly items that are added to the list.
Now I know there are some duplications of this very question which I read them all. But none of them could solve my issue completely.
My question is: How can I prevent the re-render of the entire list each time a new item is added to it, WITHOUT using memo and useMemo in any shapes and forms? Meaning that whenever a new item is added to the list, only the new item is rendered and the other items on the list remain completely untouched. Again, I'm not able to use memo or useMemo to solve this.
For example:
Current behavior is like this:
I type Test in the input
Click the Add button
A new item gets added to the list below with a random number in front of it:
• Test 0.8025874545033296
I type AnotherTest in the input
Click the Add button
A new item gets added to the list below with a random number in front of it, BUT the random number in the front of the first item is also modified:
• Test 0.4454662757698613
• AnotherTest 0.16319305763152014
Expected behavior should be like this:
I type Test in the input
Click the Add button
A new item gets added to the list below with a random number in front of it:
• Test 0.8025874545033296
I type AnotherTest in the input
Click the Add button
A new item gets added to the list below with a random number in front of it, AND the random number in the front of the first item is NOT modified. Meaning that it's NOT re-rendered:
• Test 0.8025874545033296
• AnotherTest 0.16319305763152014
UPDATE:
This question was asked from me in a coding interview. They explicitly mentioned that I'm not allowed to use memo or useMemo in the sense that these are considered cheating! Now I don't know exactly why they think in such way but I'm sure there's an specific answer in their mind that is not how React is supposed to behave.
You've said:
This question was asked from me in a coding interview. They explicitly mentioned that I'm not allowed to use memo or useMemo in the sense that these are considered cheating! Now I don't know exactly why they think in such way but I'm sure there's an specific answer in their mind that is not how React is supposed to behave.
A reasonable answer to that — quite possibly the one they were expecting — is something along the lines of: "You can do that, but it would just be reinventing memo for no good reason, and by doing something so non-standard and unusual, it would make the code hard to understand and maintain for the next person." If they actually wanted to see a solution without using those things, I would suggest you cross them off the list of places you might consider working if you have any choice (I respect the fact you may not have a choice; I remember that vividly early in my career). That's a terrible interview question if they really wanted anything other than pushback (arguably even if they were looking for pushback), which can be indicative of a bad place to work.
But again, technically, you can it with a ref by storing the rendered li elements in it (perhaps in a Map). To me, that's much more "cheating" than doing it with memo as you should, but... Here's an example:
const { useRef, useState } = React;
// *** A means of having unique keys for jobs
let nextJobId = 1;
function App() {
const inputRef = useRef(null);
const [jobs, setJobs] = useState([]);
const renderedJobs = useRef(new Map());
const jobLiElements = renderedJobs.current;
const addJob = () => {
// *** Make the jobs objects, not just strings, so the
// same string can be used by more than one job and
// so we can give the job a unique ID.
const newJob = {
id: nextJobId++,
value: inputRef.current.value,
};
if (newJob) {
setJobs((prevJobs) => [...prevJobs, newJob]);
}
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={addJob}>Add</button>
<ul>
{jobs.map((job) => {
// *** Reuse li elements you already have if you have them,
// adding new ones if you don't
let li = jobLiElements.get(job);
if (!li) {
const listItem = job.value + " " + Math.random();
console.log(`Rendering li for ${job.value}`);
// *** You can't use `listItem` as the key, since there's
// _some_ chance of the same `li` having the same text and
// random number. Use the job object instead.
li = <li key={job.id}>{listItem}</li>;
jobLiElements.set(job, li);
}
return li;
})}
</ul>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
But even if I went so far as to show them that, I'd also show them a solution using memo and putting the random value in the state of the job, talking about the strengths of doing it that way (not least that it's the normal, expected way to do this):
const { useRef, useState } = React;
const JobItem = React.memo(({ job: { value, rand } }) => {
const text = `${value} ${rand}`;
console.log(`Rendering JobItem for "${text}"`);
return <li>{text}</li>;
});
// *** A means of having unique keys for jobs
let nextJobId = 1;
function App() {
const inputRef = useRef(null);
const [jobs, setJobs] = useState([]);
const addJob = () => {
// *** Make the jobs objects, not just strings, so the
// same string can be used by more than one job, and so
// we can assign it a unique ID to use as a key.
// *** Assign the random number once, as part of the job.
const newJob = {
id: nextJobId++,
value: inputRef.current.value,
rand: Math.random(),
};
if (newJob) {
setJobs((prevJobs) => [...prevJobs, newJob]);
}
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={addJob}>Add</button>
<ul>
{/* *** Now just use JobItem*/}
{jobs.map((job) => (
<JobItem key={job.id} job={job} />
))}
</ul>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
I'm sorry that this question has been asked in a similar fashion. I've read through a lot of similar threads but just don't know enough to apply it to my little project. I'm very new to React so I would appreciate any help!
Getting to it, I'm making a pokemon game where someone can hit click buttons to filter down a JSON list of pokemon. They can go by type,weaknessess,etc.
But I'm a little confused in keeping a 'global array' if that's the right word. It may be something I don't understand with React's useState.
Here's my code
import React, { useState, useEffect } from "react";
import "./App.css";
import PokemonLibrary from "./data/PokemonList.json";
export default function App() {
const [pokemonData, setPokemonData] = React.useState(PokemonLibrary.pokemon);
const pokeListCopy = PokemonLibrary.pokemon;
const filterTypeOne = () => { //filter type
const myFilteredPoke = pokeListCopy.filter((pokeType) => {
return pokeType.type.includes("Grass");
});
console.log(myFilteredPoke); // shows array of objects of left over pokemon
setPokemonData(myFilteredPoke);
};
const filterWeakness = () => { //filter weakness
const myFilteredPoke = pokeListCopy.filter((pokeType) => {
return pokeType.weaknesses.includes("Ice");
});
setPokemonData(myFilteredPoke);
};
return (
<div className="App">
<h1>Pokemon Selector!</h1>
<div>
<button onClick={filterTypeOne}>Filter Grass</button>
<button onClick={filterWeakness}>Weak to Ice</button>
</div>
{pokemonData &&
pokemonData.map((poke) => (
<p key={poke.id}>
#{poke.num} | {poke.name} | {poke.type[0]} {poke.type[1]}
<img src={poke.img} alt="Pokemon Images"></img>
</p>
))}
</div>
);
}
My question is, how do I keep a consistent array for these two functions (and more) to pull the same data from? I'd like it to be able to be filtered in either order. But currently, these filter separate arrays. I've played with it a lot using normal JavaScript, but I can't quite figure this out.
I hope that was enough information. Please let me know if this didn't make sense! I'd appreciate any guidance. Thank you.
Problem
You are facing this problem because you try to set the state of the list in an "imperative" manner, but React is meant to be used more "declarative". That means, what you try here is like:
"if I click the button, change the list to contain the items that contain 'Grass'",
but how you should use React is:
"if I click the button, then the state of my component should be that the list only contains items with 'grass'"
That might sound like the same, but there is a subtle difference. React is good in changing a state dependent on some other state. You might say "but that's what I'm trying to do, changing the state of the list", but then you have tell the full story
"if I click the button, and the list is not filtered already, and maybe contains the items X, then change the list to contain the items that contain 'Grass', unless ..."
This becomes quite complicated, especially comparing contents of lists and components.
Solution
There are different solutions to your problem, but what you should do is basically something like:
set the component state to describe what you want
have other parts of your program (and React) take care to give you a the list dependent on this description
e.g.:
const [pokemonData, setPokemonData] = React.useState(PokemonLibrary.pokemon);
const [listState, setListState] = React.useState({ type: '', weekness: '' });
useEffect(() => {
let newList = pokemonData;
if( listState.type === 'Grass' ){
newList = newList.filter( ... );
}
if( listState.weekness === 'Ice' ){
newList = newList.filter( ... );
}
setPokemonData(newList);
}, listState );
return (
<div>
<button onClick={()=>{ setListState({ ...listState, type: 'Grass' }) }}>Filter Grass</button>
{ pokemonData.map( (poke) => ... ) }
</div>
);
(This code is not very elegant and would not even work, and should only illustrate the idea. From here on there are several ways how to implement the filtering mechanism)
I have a problem. I try to convert my string dynamically, but for some reason, it only converts the first letter of my initial string. What can I do to solve this?
Ex:
input: Mike
String_1 = 'Mike'
String_2 = 13 (I want it to be 139115, 13 for M, 9 for I, 11 for k and 5 for e).
This is my code:
import './App.css';
import React, {useState} from 'react';
import emojies from './emojies';
function App() {
let [String_1, setString_1] = useState( '' );
let [String_2, setString_2] = useState( '' );
let [Index, setIndex] = useState();
return (
<div>
<div>
<input
type="text"
value={String_1}
placeholder="Enter a message"
onChange={e => {
const val = e.target.value;
setString_1(val);
setString_2(val.toLowerCase().charCodeAt(0)-96);
}
}
/>
</div>
<div>
<p>{String_1}</p>
<p>{String_2}</p>
</div>
</div>
);
}
export default App;
This has nothing to do with React or setState.
Your issue is the logic around generating that String_2.
As you can see from the below snippet, your val.toLowerCase().charCodeAt(0)-96 only returns 13, so setState is acting correctly by passing on that value.
const val = 'Mike'
const String_2 = val.toLowerCase().charCodeAt(0) - 96
console.log(String_2)
const correct = parseInt(val.toLowerCase().split('').map(x => x.charCodeAt() - 96).join(''))
console.log(correct)
The new logic splits the string up into chars, maps over each of them to create a list of chars, then joins them together and converts into a int.
You make a mistake on setting the value for String_2.
Try this.
setString_2(val.split("").map(c => {
return c.toLowerCase().charCodeAt(0) - 96;
}).join(""));
Pass your string value from input box to this function. It iterates over all the alphabets from the string and convert them to their idx + 1 and join everything.
const convertStringToCharIdxString = (str) => {
const calcCharIdx = (char) => char.charCodeAt(0) - 97 + 1; // + 1, because you are considering a as 1
return str.split('').map(calcCharIdx).join('');
}
What is the best way to change each element of an array based on the length of the array?
For example:
User #1 input = "XYZVC"
Expected Output = "BLABL"
User #2 input = "XYZVCAD"
Expected Output = "BLABLAB"
I want B to replace index[0], L to replace index[1], and A to replace index[2]. The part I'm struggling with the most is also getting it to repeat if the user input is longer than 3.
My Attempts
I've attempted to split() the input into an array, shift() it and push() it into a new array without any luck.
I've pasted in my code below for even more detail:
import React from 'react'
class UserInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleChange = (event) => {
this.setState({value: event.target.value})
}
handleSubmit = (event) => {
alert('Your input has been submitted: ' + this.state.value)
event.preventDefault()
}
badRobot = () => {
//Check length of user input
//Add B, L, A, matching the length of the user input
//Store it within a new variable
//Plug that into the bad robots output
let checkedInput = this.state.value
let checkedArray = checkedInput.split('')
const newArr = checkedInput.shift()
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit}>
<label>
<p>Say Something</p>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<div>
<h3>Good Robot</h3>
<p>I hear you saying {this.state.value}. Is that correct?</p>
<h3>Bad Robot</h3>
<p>.</p>
<h3>Kanyebot 5000</h3>
<p>I'm gonna let you finish but Beyonce is {this.state.value}.</p>
</div>
</div>
)
}
}
export default UserInput
UPDATE
As it turns out, there is a string function, padStart (works with padEnd too) that will do exactly that pretty easily :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return ''.padStart(str.length, 'BLA');
}
console.log(mask(input1))
console.log(mask(input2))
One way to achieve it is to use the repeat function and then cut out your string depending on the original string length :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return 'BLA'.repeat(str.length / 3 + 1).substring(0, str.length)
}
console.log(mask(input1))
console.log(mask(input2))
The repeat function will repeat your string x times, as you will see in the docs linked above. Here, since your string is 3 characters long, we only need to repeat your string depending on the length of the original one, divided by 3. I am adding one since a division like 5/3 will be rounded to 1, leading to BLA even though your string is longer.
The last step, substring, will simply cut your string to the exact same length as the original one.
Here is another way of achieving it, by splitting your string into an array, and giving it the correct letter by using the modulus operator :
const input1 = "XYZVCAD"
const input2 = "erkjguherkluagheruhg"
function mask(str){
return str.split('').map((ch, index) => 'BLA'[index % 3]).join('')
}
console.log(mask(input1))
console.log(mask(input2))
This isn't code, but it's the approach I would take:
Split the input into a char array
For loop through it, with index starting at 0
If index + 1 MOD 3 = 0, set value to 'A' (array[i+1]%3 === 0)
If index + 1 MOD 2 = 0, set value to 'L'
Otherwise set value to 'B'
Return a string of the joined array items (array.join)
var myString = "somestringofsomelength";
var replacementsArr=["B","L","A"];
function replacer(str){
repString="";
for(var i=0; i<str.length;i++){
repString+=replacementsArr[i % replacementsArr.length];
}
return repString;
}
console.log(replacer(myString));
That's the way I would do it. It's using remainder division to partition the string from being 12345678 in index relative to replacementsArr to 12312312
You could change it straight away
const letters = ['B','L','A'];
handleChange = (event) => {
const length = event.target.value.length;
const letter = letters[length%3];
this.setState({value: this.state.value+letter});
}