Updating an array with useState in a form - reactjs

In the below, I have verified that setNewItem works, however items doesn't get updated so there must be an issue with the function handleSubmit. What is going wrong here?
import "./styles.css";
import React, {useState, useEffect} from 'react';
export default function App() {
const [items, setItems] = useState(['first item']);
const [newItem, setNewItem] = useState("");
const handleSubmit = (event, newItem, items) => {
event.preventDefault();
setItems([ newItem, ...items]);
};
const handleChange = (event) => {
setNewItem(event.target.value);
}
return (
<div>
<form>
<input type="text"
value={newItem}
onChange={handleChange}
/>
<input type="submit"
value="submit"
onSubmit={handleSubmit}
/>
</form>
<ul>
{items.map( (i) => {
return(<li>{i}</li>)
})}
</ul>
</div>
);
}
https://codesandbox.io/s/new?file=/src/App.js:0-797

try this 👇
import "./styles.css";
import React, { useState, useEffect } from "react";
export default function App() {
const [items, setItems] = useState(["first item"]);
const [newItem, setNewItem] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
console.log("here");
setItems([newItem, ...items]);
};
const handleChange = (event) => {
setNewItem(event.target.value);
};
return (
<div>
<form>
<input type="text" value={newItem} onChange={handleChange} />
<input type="button" value="submit" onClick={handleSubmit} />
</form>
<ul>
{items.map((i) => {
return <li>{i}</li>;
})}
</ul>
</div>
);
}

You need to change and try this.
const handleSubmit = (event) => {
event.preventDefault();
setItems([ newItem, ...items]);
};
...
...
...
<input type="submit"
value="submit"
onSubmit={event => handleSubmit(event)}
/>

Related

How to prevent re-rendering on onChange Event of input field

/**
Make sure we keep the same functionality, without causing a re-render
on every keypress when typing in the input.
*/
import React, { useState } from "react";
export const Example: React.FC = () => {
const [userInput, setUserInput] = useState("");
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setUserInput(event.target.value);
}
console.log("Example renders");
return (
<>
<h1>Send us some feedback</h1>
<div>
<input type="text" onChange={handleChange} value={userInput} />
<button
type="submit"
onClick={() => {
alert(userInput);
}}
>
Submit
</button>
</div>
</>
);
};

Why is my check complete button is not working

I wanna make the function that whenever i click on the complete button, the complete state will turn true and there will be a line through in my todo. However, my function is not working and i don't know why? can anyone help me? Thank you so much!
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now() }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 complete ? "line-through" : "">{todo.value}</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={()=>setComplete(!complete)}>complete</button>
</div>
))}
</div>
);
}
export default App;
Sandbox link: https://codesandbox.io/s/stoic-mendeleev-6fetl?file=/src/App.js
complete state missing
h3 add style={{ textDecoration: complete ? "line-through" : "" }}
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
const [complete, setComplete] = useState(false);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now() }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 style={{ textDecoration: complete ? "line-through" : "" }}>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={() => setComplete((perv) => !perv)}>complete</button>
</div>
))}
</div>
);
}
export default App;
Try this
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
const [complete, setComplete] = useState({});
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
const id = Date.now();
setTodos([...todos, { value, id }]);
setComplete({ ...complete, [id]: false });
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
console.log(complete);
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 className={complete[todo.id] ? "line-through" : ""}>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button
onClick={() =>
setComplete({ ...complete, [todo.id]: !complete[todo.id] })
}
>
complete
</button>
</div>
))}
</div>
);
}
export default App;
*Note you will need to specify styling for your line-through class to see the strike through
I have updated the codesandbox. you can have look on the codesandbox.
https://codesandbox.io/s/compassionate-frost-8255y
Try below code.
You need to create setComplete menthod.
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now(), complete: false }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
const handleComplete = (id) => {
let compTodo = todos.filter((todo) => todo.id === id);
compTodo[0].complete = true;
let allTodo = todos.filter((todo) => todo.id !== id);
setTodos([...allTodo, compTodo[0]]);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => {
return (
<div key={todo.id}>
<h3
style={{
textDecoration: todo.complete ? "line-through" : "none"
}}
>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={() => handleComplete(todo.id)}>complete</button>
</div>
);
})}
</div>
);
}
export default App;

Rendering content in react js on submit button

I'm fetching content from external api using axios and react hooks.
Currently, I'm rendering {renderedResults} without any button. But I want to add submit button and then only render the results when someone clicks on it.
How to implement it in this scenario?
I tried official doc..but no success...
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./Search.css";
const Search = () => {
const [vpincode, setvPincode] = useState("");
const [vdate, setvDate] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
const search = async () => {
const { data } = await axios.get("https://api", {
params: {
pincode: vpincode,
date: vdate,
},
});
setResults(data.sessions);
};
search();
}, [vpincode, vdate]);
const renderedResults = results.map((result) => {
return (
<div>
{result.name}
{result.address}
</div>
);
});
return (
<div className="container">
<div className="w-25 mb-3">
<label className="form-label ">Enter Pincode:</label>
<input
value={vpincode}
type="text"
className="form-control"
placeholder="Pincode"
onChange={(e) => setvPincode(e.target.value)}
></input>
</div>
<div className="w-25 mb-3">
<label className="form-label">Date:</label>
<input
value={vdate}
type="text"
className="form-control"
placeholder="Date"
onChange={(e) => setvDate(e.target.value)}
></input>
</div>
{renderedResults}
</div>
);
};
export default Search;
Code not tested, but you can do something like this...
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./Search.css";
const Search = () => {
const [vpincode, setvPincode] = useState("");
const [vdate, setvDate] = useState("");
const [results, setResults] = useState([]);
const fetchApiContent = async (_) => {
const { data } = await axios.get("https://api", {
params: {
pincode: vpincode,
date: vdate,
},
});
setResults(data.sessions);
}
const renderedResults = results.map((result) => {
return (
<div>
{result.name}
{result.address}
</div>
);
});
return (
<div className="container">
<div className="w-25 mb-3">
<label className="form-label ">Enter Pincode:</label>
<input
value={vpincode}
type="text"
className="form-control"
placeholder="Pincode"
onChange={(e) => setvPincode(e.target.value)}
></input>
</div>
<div className="w-25 mb-3">
<label className="form-label">Date:</label>
<input
value={vdate}
type="text"
className="form-control"
placeholder="Date"
onChange={(e) => setvDate(e.target.value)}
></input>
</div>
{renderedResults}
<button onClick={fetchApiContent}>Fetch API Content</button>
</div>
);
};
export default Search;

custom hook to simplify react controlled form

i'd like to simplify form creation avoiding to write for each fied value={} and onChange={} using a custom hook.
this is my code:
https://codesandbox.io/s/busy-noether-pql8x?file=/src/App.js
the problem is that, each time i press the button, the state is cleaned except for the field i've currently edited
import React, { useState } from "react";
import "./styles.css";
const useFormField = (initialValue) => {
const [values, setValues] = React.useState(initialValue);
const onChange = React.useCallback((e) => {
const name = e.target.name;
const value = e.target.value;
setValues( (prevValues) => ({...prevValues,[name]:value}));
}, []);
return { values, onChange };
};
export default function App() {
//hoot to simplify the code,
const {values,onChange} = useFormField({
Salary: "",
Email: ""
})
const onCreateUser = () => {
console.log(values);
};
return (
<div className="App">
<form>
<label htmlFor="Email">Email: </label>
<input name="Email" onChange={onChange} />
<label htmlFor="Email">Salary: </label>
<input name="Salary" onChange={onChange} />
<button type="button" onClick={onCreateUser}>
Send
</button>
</form>
</div>
);
}

Random Component Behavior

I have 2 components, the problem is that on the first submit click i cant setUser(), (although addUser arguments are giving the correct values) it keeps the original state '', '', but if i click it again it change correctly. I don't know what I'm doing wrong, its my first question sorry if its poorly formatted.
import React, { useState, useEffect } from "react";
import "./notes.css";
import UserNameMailForm from "./userNameMailForm";
const NoteApp = props => {
const [user, setUser] = useState({
userName: "",
email: ""
});
const addUser = (userName, email) => {
const newUser = { userName, email };
setUser(newUser);
console.log(user);
console.log(userName, email);
};
return (
<div className="container p-0">
<div className="screen pt-2">
<p>Users</p>
</div>
<UserNameMailForm addUser={addUser} />
</div>
);
};
export default NoteApp;
The second component is this one:
import React, { useState, useEffect } from "react";
const UserNameMailForm = ({ addUser }) => {
const [userName, setUsername] = useState("");
const [email, setEmail] = useState("");
useEffect(() => {}, []);
const handleSubmit = e => {
e.preventDefault();
addUser(userName, email);
};
return (
<form onSubmit={handleSubmit} className="form-group">
<input
type="text"
className="form-control"
placeholder="User name"
value={userName}
onChange={e => setUsername(e.currentTarget.value)}
/>
<input
type="text"
className="form-control"
placeholder="email"
value={email}
onChange={e => setEmail(e.currentTarget.value)}
/>
<button type="submit" className="btn btn-outline-danger">
Add
</button>
</form>
);
};
export default UserNameMailForm;
You code is working fine, as this example demonstrates:
const { useState } = React;
const NoteApp = props => {
const [user, setUser] = useState({
userName: "",
email: ""
});
const addUser = (userName, email) => {
const newUser = { userName, email };
setUser(newUser);
};
return (
<div className="container p-0">
<div className="screen pt-2">
<p>Users</p>
{JSON.stringify(user)}
</div>
<UserNameMailForm addUser={addUser} />
</div>
);
};
const UserNameMailForm = ({ addUser }) => {
const [userName, setUsername] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = e => {
e.preventDefault();
addUser(userName, email);
};
return (
<form onSubmit={handleSubmit} className="form-group">
<input
type="text"
className="form-control"
placeholder="User name"
value={userName}
onChange={e => setUsername(e.currentTarget.value)}
/>
<input
type="text"
className="form-control"
placeholder="email"
value={email}
onChange={e => setEmail(e.currentTarget.value)}
/>
<button type="submit" className="btn btn-outline-danger">
Add
</button>
</form>
);
};
ReactDOM.render(<NoteApp />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
The issue is that setUser is asynchronous, and user is a reference to the previous user object, which will be the object you pass as initial value to useState, so that's why console.log(user); is giving you the previous state.

Resources