How Can I use current value of a variable in somewhere else? - javascript

I change the name and age dynamiclly by creating two handlers. When I input an age it changes dynamicly. There is no problem here. But the name goes default as well. Because I defined it as a hard-coded value in 'ageChangeHandler'.But i want the name to stay like I did in the 'nameChangeHandler'. Is there something like 'currentValue' or 'lastValue' in Javascript/ES6?
I hope I explained it properly.
Thanks in advance.
I couldnt find any options like currentvalue etc.
nameChangeHandler = (event) =>{
this.setState({
persons: [
{name: "Max" , age: 28},
{name: event.target.value, age: 29},
{name:"Arthur", age:34}
]
})
}
ageChangeHandler = (event) =>{
this.setState({
persons:[
{name: "Max" , age: 28},
{name: "Tom", age: 29},
{name:"Arthur", age:event.target.value}
]
})}
Lines I call handlers:
<Person
name={this.state.persons[0].name}
age={this.state.persons[0].age}/>
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
click={this.switchNameHandler.bind(this, "Way 2")}
Namechanged={this.nameChangeHandler}>My hobbies: coding</Person>
<Person
name={this.state.persons[2].name}
age={this.state.persons[2].age}
Agechanged={this.ageChangeHandler}/>

If you pass a function, instead of an object to setState, you get this signature:
this.setState((prevState, props) => {
// do something with prevState
return { ...prevState }
});
allowing you to use the previous state to create the new one.
so In your case:
this.setState(prevState => {
return {
...prevState,
persons: prevState.persons.map(person => {
if (person.name === "Arthur") return { ...person, age: e.target.value }
return person
})
}
})
or something like that.

You can use an index to dynamically select a user to edit and then just update its attributes:
nameChangeHandler = (event) => {
const { index } = this.state;
this.setState({
persons: this.state.persons.map((item, j) => {
if (j === index) {
return {
...item,
name: event.target.value,
};
} else {
return item;
}
}),
});
}
You could also create a universal function to update any number of parameters:
<input
type="text"
// Here we send a value, n index (can be from state) and the parameter to update
onChange={(e) => { this.changeHandler(e.target.value, 0, 'name') }}
/>
changeHandler = (value, index, attr) => {
// index is the object in the array that we want to update
// attr is the key
this.setState({
persons: this.state.persons.map((item, j) => {
if (j === index) {
return {
...item,
[attr]: value,
};
} else {
return item;
}
}),
});
}

Declared an ID for all of persons :
state = {
persons: [
{id:"25343", name:"Max", age: 28},
{id:"24323ad ", name: "Tom", age: 29},
{id:"asdavd231", name:"Arthur", age:34}
],
Updated them with their ID's :
ageChangeHandler = (event,id) =>{
const personIndex = this.state.persons.findIndex(p=>{
return p.id === id;
})
const person = {
...this.state.persons[personIndex]
}
person.age = event.target.value;
const persons = [...this.state.persons]
persons[personIndex] = person;
this.setState({persons:persons})}
Rendered them in a map:
<div>
{this.state.persons.map((person,index)=>{
return <Person
click={() => this.deletePersonHandler(index)} //map'ten gelen index deletePersonHandler'a pareametre olarak gönderiliyor
name={person.name} //map'in 1.parametresi. (persons'u işaret ediyor)
age={person.age}
key={person.id}
nameChanged={(event)=>this.nameChangeHandler(event,person.id)}
ageChanged={(event)=>this.ageChangeHandler(event,person.id)}/>
})}
</div>

Related

How to populate dynamic form field values?

So in my application user have an option to enter a number, for example if user enters as "5" then it will add 5 textInputs. By this, i have successfully looped the textInput. Now, how to populate or store the values in the state ?
My code :
this.state =
{
usersDetails: [{name: "", age: "", gender: "", primary: false}, {name: "", age: "", gender: "", primary: false}]
};
handleChange(i, e) {
const { name, value } = e;
let users = [...this.state.usersDetails];
users[i] = {...users[i], [name]: value};
this.setState({ usersDetails });
console.log(this.state.usersDetails);
}
let items = [];
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
placeholder="Enter Name"
onChangeText={this.handleChange.bind(this, i)}
value={this.state.userDetails}
/>)
}
With the help of stockoverflow i created the handleChange function but it gives error ! How to populate the values and save on the state for dynamic form !
Kindly guide !
handleChange = (value, index,key) => {
this.setState((prevState) => ({
...prevState,
usersDetails: prevState.usersDetails.map((val, mapIndex) => {
if (mapIndex === index) {
val[key] = value;
return val;
}
return val;
}),
}));
};
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
key={i}
placeholder="Enter Name"
onChangeText={(text) => {
handleChange(text, i,'name'); // here you can pass name,age whatever
}}
value={this.state.userDetails[i].name}
/>,
);
}

How to set a counter for duplicate values in React?

My code is basically a form with a text input and a submit button. Each time the user input data, my code adds it to an array and shows it under the form.
It is working fine; however, when I add duplicate values, it still adds it to the list. I want my code to count these duplicates and show them next to each input.
For example, if I input two "Hello" and one "Hi" I want my result to be like this:
2 Hello
1 Hi
Here is my code
import React from 'react';
import ShoppingItem from './ShoppingItem';
class ShoppingList extends React.Component {
constructor (props){
super(props);
this.state ={
shoppingCart: [],
newItem :'',
counter: 0 };
}
handleChange =(e) =>
{
this.setState ({newItem: e.target.value });
}
handleSubmit = (e) =>
{
e.preventDefault();
let newList;
let myItem ={
name: this.state.newItem,
id:Date.now()
}
if(!this.state.shoppingCart.includes(myItem.name))
{
newList = this.state.shoppingCart.concat(myItem);
}
if (this.state.newItem !=='')
{
this.setState(
{
shoppingCart: newList
}
);
}
this.state.newItem ="" ;
}
the rest of my code is like this:
render(){
return(
<div className = "App">
<form onSubmit = {this.handleSubmit}>
<h6>Add New Item</h6>
<input type = "text" value = {this.state.newItem} onChange ={this.handleChange}/>
<button type = "submit">Add to Shopping list</button>
</form>
<ul>
{this.state.shoppingCart.map(item =>(
<ShoppingItem item={item} key={item.id} />
)
)}
</ul>
</div>
);
}
}
export default ShoppingList;
Issues
this.state.shoppingCart is an array of objects, so this.state.shoppingCart.includes(myItem.name) will always return false as it won't find a value that is a string.
this.state.newItem = ""; is a state mutation
Solution
Check the newItem state first, if empty then return early
Search this.state.shoppingCart for the index of the first matching item by name property
If found then you want to map the cart to a new array and then also copy the item into a new object reference and update the quantity.
If not found then copy the array and append a new object to the end with an initial quantity 1 property.
Update the shopping cart and newItem state.
Code
handleSubmit = (e) => {
e.preventDefault();
if (!this.state.newItem) return;
let newList;
const itemIndex = this.state.shoppingCart.findIndex(
(item) => item.name === this.state.newItem
);
if (itemIndex !== -1) {
newList = this.state.shoppingCart.map((item, index) =>
index === itemIndex
? {
...item,
quantity: item.quantity + 1
}
: item
);
} else {
newList = [
...this.state.shoppingCart,
{
name: this.state.newItem,
id: Date.now(),
quantity: 1
}
];
}
this.setState({
shoppingCart: newList,
newItem: ""
});
};
Note: Remember to use item.name and item.quantity in your ShoppingItem component.
Replace your "handleSubmit" with below one and check
handleSubmit = (e) => {
e.preventDefault();
const { shoppingCart, newItem } = this.state;
const isInCart = shoppingCart.some(({ itemName }) => itemName === newItem);
let updatedCart = [];
let numberOfSameItem = 1;
if (!isInCart && newItem) {
updatedCart = [
...shoppingCart,
{
name: `${numberOfSameItem} ${newItem}`,
id: Date.now(),
itemName: newItem,
counter: numberOfSameItem
}
];
} else if (isInCart && newItem) {
updatedCart = shoppingCart.map((item) => {
const { itemName, counter } = item;
if (itemName === newItem) {
numberOfSameItem = counter + 1;
return {
...item,
name: `${numberOfSameItem} ${itemName}`,
itemName,
counter: numberOfSameItem
};
}
return item;
});
}
this.setState({
shoppingCart: updatedCart,
newItem: ""
});
};

React useState failing to update two way binding

I'm trying to set up so that when I type a name in the input of the Person component the state in the App component is updated and in turn updates the value of a prop in Person, however it appears the state change is happening, but the prop isn't updated, can anyone help me figure out what is wrong?
App
const App = () => {
const [persons, setPersons] = useState([
{id: "key1", name: "Daniel", age: "28"},
{id: "key2", name: "John", age: "30"},
{id: "key3", name: "Doe", age: "60"}
]);
const nameChangedHandler = (id, event) => {
console.log(event.target.value);
console.log(id);
const personIndex = persons.findIndex(p => {
return p.id === id;
});
const person = {
...persons[personIndex]
};
person.name = event.target.value;
const pers = [...persons];
persons[personIndex] = person;
setPersons(pers);
console.log(persons);
};
let people = persons.map((person, index) => {
return (
<Person
name={person.name}
key={person.id}
age={person.age}
changed={nameChangedHandler.bind(this, person.id)}
/>
);
});
return <div className="App">{people}</div>;
};
Person
const person = props => (
<div className={style.Person}>
<p onClick={props.click}>
I'm {props.name}, and I am {props.age}!
</p>
<input type="text" onChange={props.changed} value={props.name} />
</div>
);
You are assigning to the wrong variable, try the following:
const pers = [...persons];
pers[personIndex] = person;
And it should work as expected. Since you were updating your state object persons instead of the object you cloned pers, which you used to set the state, your console log was showing the expected output but your state wasn't being updated properly.
Check this working stackblitz
To be honest, I would use a simple map function to change the name of the particular person.
Inside nameChangedHandler function:
const updatedPersons = persons
.map((person) => person.id === id ? {...person, name: event.target.value} : person);
and then update the local state
setPersons(updatedPersons);
It should work as expected.

Copy object from one array of objects to another in React

I have this.props.person like this:
person = [{id: 1, name: John, age: 20},
{id: 2, name: Kate, age: 30},
{id: 3, name: Mike, age: 25}]
And I have empty this.state.newPerson: [].
Function get id value, searching object with this id in this.props.person, and add to this.state.newPerson this object. It can be repeats a few times.
For example: I call funcion addPerson twice with id=1 and id=3. In result I should get
this.state.newPerson: [{id: 1, name: John, age: 20}, {id: 3, name: Mike, age: 25}].
I tried:
addPerson(idPerson) {
const list = this.state.newPerson;
const personToList = this.props.person.find(el => el.id === idPerson);
const newP = Object.assign(list, { personToList });
this.setState({ newPerson: newP });
}
In fact, I got something like [personToList {id: ...}]
How can I fix it?
why do you use Object.assign if this.state.newPerson is an array?
Just use
list.push(personToList) but set your state like this this.state.newPerson = [];
You want to add personToList to the list array instead of assigning the entire array with the object { personToList: personToList }.
You could use the spread syntax instead.
Example
class App extends React.Component {
state = { newPerson: [] };
addPerson = personId => {
const list = this.state.newPerson;
if (list.some(el => el.id === personId)) {
return;
}
const personToList = this.props.person.find(el => el.id === personId);
const newP = [...list, personToList];
this.setState({ newPerson: newP });
};
render() {
return (
<div style={{ display: "flex" }}>
<div>
{this.props.person.map(p => (
<div id={p.id} onClick={() => this.addPerson(p.id)}>
{p.name}
</div>
))}
</div>
<div>
{this.state.newPerson.map(p => (
<div id={p.id} onClick={() => this.addPerson(p.id)}>
{p.name}
</div>
))}
</div>
</div>
);
}
}
ReactDOM.render(
<App
person={[
{ id: 1, name: "John", age: 20 },
{ id: 2, name: "Kate", age: 30 },
{ id: 3, name: "Mike", age: 25 }
]}
/>,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Object.assign is not right for this use case scenario. You're trying to add to an array on the state.
Try this instead:
addPerson(idPerson) {
let list = [...this.state.newPerson];
let personToList = this.props.person.find(el => el.id === idPerson);
list.push(personToList);
this.setState({
newPerson: list
});
}
Object.assign is used to combine two javascript object.personToList is already an object.
const newP = Object.assign(list, personToList);
Actually,you could use push to fix it.
const newP = list.push(newP)
find returns only the first matching item. Use filter to get all of them.
Check the documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
try this:
addPerson(idPerson) {
this.setState({
newPerson : this.state.newPerson.concat(this.props.person.find( i => i.id === idPerson))
})
}
}
or
addPerson(idPerson) {
this.setState({
newPerson : [this.state.newPerson, this.props.person.find( i => i.id === idPerson)]
})
}

this.state.persons.map is not a function

I'm new to react and only understand the basic. I got this project from someone to look at, but I'm scratching my head since morning with this problem: "Uncaught TypeError: this.state.persons.map is not a function".Please if you can try to try to go over it in easy but in under the hood way.Your help would be really appreciated.
App Component
class App extends Component {
state = { //not optional name state
persons: [
{ id: "43qtf3w4", name: 'Igor', age: 19},
{ id: "445wgwre", name: 'Vasya', age: 20},
{ id: "t45wg45e",name: "Petya", age: 22}
],
otherState: 'some other value',
showPersons: false,
}
switchNameHandler = (newName) => {
this.setState( {
persons: [
{ name: newName, age: 19},
{ name: 'Vasya', age: 20},
{ name: "Petya", age: 27}
]
} )
}
nameChangedHandler = (event, id) => {
const personIndex = this.state.persons.findIndex( (person) => person.id ===id);
const person = {...this.state.persons[personIndex]};
person.name = event.target.value;
const persons = {...this.state.persons};
persons[personIndex] = person;
this.setState( {
persons: persons
} )
}
deletePersonHandler = (personIndex) => {
const persons = [...this.state.persons];
persons.splice(personIndex, 1);
this.setState( {
persons: persons,
} )
}
togglePersonsHandler = () => {
const doesShow = this.state.showPersons;
this.setState( {
showPersons: !doesShow,
} )
}
render() {
const style = {
backgroundColor: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer',
}
let persons = null;
if ( this.state.showPersons) {
persons = (
<div >
{this.state.persons.map((person, index) => {
return <Person
click={() => this.deletePersonHandler(index)}
name={person.name}
age={person.age}
key={person.id}
changed={(event) => this.nameChangedHandler( event, person.id)}/>
})}
</div>
)
}
return (
<div className="App">
<h1>Hi, I'm a React App</h1>
<p>This is realy working</p>
<button
style={style}
onClick ={ this.togglePersonsHandler}>Toggle Persons</button>
{persons}
</div>
);
}
}
Person Component
const person = (props) => {
return (
<div className="Person">
<p onClick={props.click}>I'm a {props.name} and I am {props.age} years old!</p>
<p>{props.children}</p>
<input type="text" onChange={props.changed} value={props.name}/>
</div>
)
};
In the nameChangeHandler() function you are setting persons to an object:
const persons = {...this.state.persons}
which does not have .map()

Categories

Resources