I'm trying to make a to-do list in React which I can edit and delete the to-dos. I'm trying to use this function to delete a to-do, which worked on a past project.
function deleteTodo(id) {
const updatedTodos = [...values].filter((values) => values.id !== id);
setValues(updatedTodos);}
But I'm trying to use this code on this new project and I get the error 'values is not iterable'. I imagine its because the 'values' is an object, not an array, as you can see below...
const [values, setValues] = useState({
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
});
What is the best way I can delete to-dos like this? This is my code:
export function App() {
const [values, setValues] = useState({
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
});
const [atividades, setAtividades] = useState([]);
function handleChange(event) {
setValues({
...values,
[event.target.name]: event.target.value,
[event.target.descricao]: event.target.descricao,
});
}
function handleSubmit(event) {
event.preventDefault();
setAtividades([
...atividades,
{
id: values.id,
nome: values.atividade,
descricao: values.descricao,
checkTrabalho: values.trabalho,
checkPessoal: values.pessoal,
},
]);
}
function deleteTodo(id) {
const updatedTodos = [...values].filter((values) => values.id !== id);
setValues(updatedTodos);
}
return (
// ==========================================================================================
<div className="App">
<form onSubmit={handleSubmit}>
<input
name="atividade"
type="text"
value={values.atividade}
onChange={handleChange}
/>
<input
maxLength="100"
name="descricao"
type="text"
value={values.descricao}
onChange={handleChange}
/>
<br />
<input
name="trabalho"
id="trabalho"
type="radio"
checked={values.trabalho}
onChange={() => {
setValues({ ...values, pessoal: false, trabalho: true });
}}
/>
<label htmlFor="trabalho">Trabalho</label>
<input
name="pessoal"
id="pessoal"
type="radio"
checked={values.pessoal}
onChange={() => {
setValues({ ...values, trabalho: false, pessoal: true });
}}
/>
<label htmlFor="pessoal">Pessoal</label>
<button type="submit">Criar</button>
</form>
{/* ========================================================================================== */}
{/* ========================================================================================== */}
<div key={values.id}>
<div>
<h1>Trabalhos</h1>
{atividades.map(
(atividade) =>
atividade.checkTrabalho === true && (
<div>
Todo: {atividade.nome} <br />
descricao: {atividade.descricao}
<button onClick={deleteTodo}>deletar</button>
</div>
)
)}
</div>
<div key={values.id}>
<h1>Pessoal</h1>
{atividades.map(
(atividade) =>
atividade.checkPessoal === true && (
<div>
Todo: {atividade.nome} <br />
descricao: {atividade.descricao}
<button onClick={deleteTodo}>deletar</button>
</div>
)
)}
</div>
</div>
</div>
// ==================================================================================================
);
}
It doesn't work because you have created an object instead of array of objects. If you want to store multiple todo objects you should create it like:
const [values, setValues] = useState([
{
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
}
]);
Then you can use filter to remove items just like you are doing right now.
Or assuming you are storing your todos in atividades, then you should update that array of object instead of values.
const updatedTodos = [...atividades].filter((todo) => todo.id !== id);
Related
I want to console.log() multiple values from five different dropdowns on a button click. I have done this for one dropdown, but I don't know how to do it for more. I'm still a beginner.
Here's my code:
export default function Suma() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [message, setMessage] = useState('');
const handleChange = event => {
setMessage(event);
};
const handleClick = event => {
event.preventDefault();
console.log(message);
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={handleChange}
value={message}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
};
I hope you are looking for this one:
export default function App() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
{ value: "third", label: "third" },
{ value: "fourth", label: "fourth" },
{ value: "five", label: "five" },
];
const [showAll, setShowAll ] = useState([]);
const [dropdowns,setDrodowns] = useState({
'message1': '',
'message2': '',
'message3': '',
'message4': '',
'message5': '',
});
const handleChange = (event) => {
setDrodowns({...dropdowns,[event.target.name]:event.target.value});
}
const handleClick = (event) => {
event.preventDefault(); // if you use the element inside `form` then it would prevent to submit
console.log(dropdowns);//to log the values in console
setShowAll(Object.values(dropdowns));// to show the changes in UI
}
return (
<div>
<div className="col-lg">
<Select
name="message1"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message2"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message3"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message4"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message5"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
</div>
<hr/>
<ul>
{ showAll.map((val,i)=><li key={i}>{i+1} --- {val}</li>) }
</ul>
<hr/>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}
For details check the code sandbox link
Out put
Edit: Based on user comments I edited the answer
You could pass a parameter to your handleChange.
const handleChange = (event, position) => {
console.log(position);
};
<Select onChange={(e) => handleChange(e, 1)} />
<Select onChange={(e) => handleChange(e, 2)} />
<Select onChange={(e) => handleChange(e, 3)} />
Improving axtck's answer, you can get each select value like below
import React, {useState} from 'react';
import Select from 'react-select';
export function App(props) {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [messages, setMessages] = useState([]);
const handleChange = (event, pos) => {
console.log(pos)
console.log(event.value)
let mz = [...messages];
if (mz.length > 0 && mz.findIndex(msg => msg.index == pos) > -1) {
mz[mz.findIndex(msg => msg.index == pos)] = event.value;
setMessages(mz);
}
else {
mz.push({
index: pos,
value: event.value
});
setMessages(mz);
}
};
const handleClick = event => {
event.preventDefault();
for (let i = 0; i < messages.length; i++)
console.log(messages[i].value)
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 1)}
value={messages[0] ? messages[0].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 2)}
value={messages[1] ? messages[1].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}
I have the next state:
const [social_networks, setSocial_networks] = useState([
{
social_account_type: "personal",
social_network: "linkedin",
handle: "",
content: ""
},
{
social_account_type: "company",
social_network: "twitter",
handle: "",
content: ""
},
{
social_account_type: "personal",
social_network: "webpage",
handle: "",
content: ""
}
])
In the parent component I declare the function:
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...social_networks];
list[index][name] = value;
setSocial_networks(list);
};
Set this to the children in the next code:
social_networks.map((social_network, idx) => {
if (social_network.social_account_type == "personal") return <div key={idx}><AccountsPill handle={social_network.handle} social={social_network.social_network} content={social_network.handle} index={idx} handleInputChange={handleInputChange} /> </div>
})
And into my child component I have the next code:
<div className="row m-0">
<div className="svg-container col-md-1">
<BrowserIcon color="#868E96" />
</div>
<input type="text" className="col-md-11 set-account-input" placeholder=
{"www."+props.social+".com"} name="handle" id="handle" defaultValue={props.handle}
onChange={e => props.handleInputChange(e, props.index)} />
</div>
<div className="row m-0">
<div className="svg-container col-md-1">
<AtIcon color="#868E96" />
</div>
<input type="text" className="col-md-11 set-account-input" placeholder="MyUsername"
name="content" id="content" defaultValue={props.content} onChange={e =>
props.handleInputChange(e, props.index)} />
</div>
The page show me like that:
after rendering frontpage
When I change the input.Content works fine:
input.name=content change
But, if I change the input.name=handle , change the other input too:
input.name=handle change
I tried to make two differents handleChange functions, change the props.name, add the props.id, but does'nt works yet.
You passed wrong content props to your AccountsPill component, it should be
<AccountsPill
handle={social_network.handle}
social={social_network.social_network}
content={social_network.content}
index={idx}
handleInputChange={handleInputChange}
/>
I think your problem is that const list = [...social_networks]; shallow copies the state array, so it's really just an array of the original state objects. Try instead:
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = social_networks.map((social, i)=>{
if(index === i){
return {...social, [name]: value}
}
return {...social}
})
setSocial_networks(list);
};
In my react app, I have created a page named Add Product with a button named Add Variation to allow adding small, medium, large variations of a product but can't figure out how to remove the small, medium, or large variation object from the state if user changes their mind.
Here's a summary of the problem:
Here's what the component looks like now:
const AddProduct = () => {
const [addVar, setAddVar] = useState(0)
const [values, setValues] = useState({
name: "",
description: "",
categories: [],
category: "",
photo: "",
loading: false,
error: "",
createdProduct: "",
redirectToProfile: false,
variations: [],
formData: ""
});
const {
name,
description,
price,
categories,
category,
photo,
loading,
error,
createdProduct,
redirectToProfile,
variations,
formData
} = values;
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
const handleVariationChange = (name, numberVal) => event => {
// numberVal is the iteration number
// name is the variation property which can be vname, vprice, vshipping, vquantity
// these are tested next in the following if statements
const value = event.target.value;
console.log(`numberVal: `, numberVal);
event.preventDefault()
let newVariations = Array.from(variations)
if(name === "vname") {
newVariations[numberVal].vname = value;
console.log(`newVariations[numberVal].vname value: `, newVariations)
}
if(name === "vprice") {
newVariations[numberVal].vprice = value;
console.log(`newVariations[numberVal].vprice value: `, newVariations)
}
if(name === "vshipping") {
newVariations[numberVal].vshipping = value;
console.log(`newVariations[numberVal].vshipping value: `, newVariations)
}
if(name === "vquantity") {
newVariations[numberVal].vquantity = value;
console.log(`newVariations[numberVal].vquantity value: `, newVariations)
}
setValues({...values, variations: newVariations})
formData.set("variations", JSON.stringify(newVariations));
};
const removeVariation = (e) => {
e.preventDefault()
let newVariations = Array.from(variations)
let popped = newVariations.pop()
setValues({
...values,
variations: newVariations,
error: ""
})
}
const newPostForm = () => (
<form className="mb-3" onSubmit={clickSubmit}>
<h4>Main Photo</h4>
<div className="form-group">
<label className="btn btn-secondary">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image/*"
/>
</label>
</div>
<div className="form-group">
<label className="text-muted">Main Product Name</label>
<input
onChange={handleChange("name")}
type="text"
className="form-control"
value={name}
placeholder="Add main product name"
/>
</div>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea
onChange={handleChange("description")}
className="form-control"
value={description}
placeholder="Add description"
/>
</div>
<div className="form-group">
<label className="text-muted">Category</label>
<select
onChange={handleChange("category")}
className="form-control"
>
<option>Please select</option>
{categories &&
categories.map((c, i) => (
<option key={i} value={c._id}>
{c.name}
</option>
))}
</select>
</div>
<div>
<button onClick={addVariation}>Add variation</button>
</div>
{variations ? VariationComponent() : null}
<br />
<br />
<button type="submit" className="btn btn-outline-primary">Create Product</button>
</form>
);
return (
<Layout>
<div className="row">
<div className="col-md-8 offset-md-2">
{newPostForm()}
</div>
</div>
</Layout>
);
};
export default AddProduct;
Every time Add variation is clicked, another VariationComponent form is appended to the page . For example, if Add variation button was clicked 3 times, it would result in 3 VariationComponent forms with 3 attached Remove variation buttons. Unfortunately, I do not see how to tell React the position of the #2 item in variations to remove it so I resorted to solving this with .pop(), which is not what I want.
How can I tell React to remove the right array item when Remove variation button is clicked?
If I understand correctly, you can use Arrray.filter() determine which variation to remove. It returns a new array with all but the matching numberVal.
onClick={e=>removeVariation(e)}
const removeVariation = e => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.name !== e.target.value),
error: ''
});
};
Thanks to #RobinZigmond's and #7iiBob's answers, I was able to solve this by this code:
const removeVariation = (e, num) => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.number !== num),
error: ''
});
};
Remove variation button:
<button onClick={(e) => removeVariation(e, variations[i].number)} className="btn-danger">
{`Remove Variation`}
</button>
Keep in mind the empty variation object looks like this:
{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}
and n is coming from addVariation here:
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
Wholehearted thank you as this cost me hours of headache!
I make form using Formik in my app. When I send form to my local server I create image with title. Attach images I should using input type="file".But I have very little experience using the formik.
What I should write in propTypes when in form I use input type="file' in file InputImage.js?
And How to add input type="file" in file AddImage.js in mark place?
Now I want to create input which attach image component InputImage.js similar to InputTitle.js.
I comment line where I dont know what I should write.
AddImage.js:
const AddImage = (props) => {
const {handleSubmit, values, handleChange} = useFormik({
initialValues: {
title: '',
image: '' // Did I write correctly here?
},
validateOnchange: false,
onSubmit: async (formValues) => {
const response = await api(`${imageRoutePath}`, {
method:'POST',
body: JSON.stringify(formValues),
});},
});
return (
<div>
<form onSubmit={handleSubmit}>
<InputTitle
label="title"
id="title"
inputProps={{
name:'title',
value: values.title,
onChange: handleChange,
}}
/>
<InputImage
label="image"
id="image"
inputProps={{
name:'image',
// WHAT I SHOULD WRITE THERE?
onChange: handleChange,
}}
/>
<button type="submit" disabled={isSubmitting}>Add</button>
</form>
</div>
);
};
export default AddImage;
InputImage.js:
const InputImage = ({
label, inputProps, error, id,
}) => (
<div className="formInputCategory">
<label htmlFor={id} className="formInputLabelCategory">
{label}
</label>
<input {...inputProps} id={id} />
{error && <span className="formInputErrorCategory">{error}</span>}
</div>
);
InputImage.propTypes = {
label: PropTypes.string.isRequired,
// WHAT I SHOULD WRITE THERE?
error: PropTypes.string,
id: PropTypes.string.isRequired,
};
InputImage.defaultProps = {
error: '',
}
---------------------------------------------------------------------------------------
example how I write InputTitle.js:
const InputTitle = ({
label, inputProps, error, id,
}) => (
<div className="formInputCategory">
<label htmlFor={id} className="formInputLabelCategory">
{label}
</label>
<input {...inputProps} id={id} />
{error && <span className="formInputErrorCategory">{error}</span>}
</div>
);
InputTitle.propTypes = {
label: PropTypes.string.isRequired,
inputProps: PropTypes.instanceOf(Object).isRequired,
error: PropTypes.string,
id: PropTypes.string.isRequired,
};
InputTitle.defaultProps = {
error: '',
}
Formik doesnot support fileupload by default, But you can try the following
<input id="file" name="file" type="file" onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}} />
Here "file" represents the key that you are using for holding the file
setFieldValue is obtained from <Formik />
reference : formik setFieldValue prop
your code will look like :
const AddImage = (props) => {
const {handleSubmit, values, handleChange, setFieldValue } = useFormik({
initialValues: {
title: '',
image: '' // Did I write correctly here?
},
validateOnchange: false,
onSubmit: async (formValues) => {
const response = await api(`${imageRoutePath}`, {
method:'POST',
body: JSON.stringify(formValues),
});},
});
return (
<div>
<form onSubmit={handleSubmit}>
<InputTitle
label="title"
id="title"
inputProps={{
name:'title',
value: values.title,
onChange: handleChange,
}}
/>
<InputImage
label="image"
id="image"
inputProps={{
name:'file',
id="file",
// WHAT I SHOULD WRITE THERE?
type="file",
onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}},
}}
/>
<button type="submit" disabled={isSubmitting}>Add</button>
</form>
</div>
);
};
export default AddImage;
I am trying to edit the value from table and put it in textbox. When I Click edit button it says "Cannot read property edit name of undefined". I have used fat arrow functions. I also used bind in the constructor but it has same error. Below is My Code. When Clicked on editName button, it gives error.
class App extends React.Component {
constructor(props) {
super(props);
this.onNameChange = this.onNameChange.bind(this);
this.onSurnameChange = this.onSurnameChange.bind(this);
this.onIdChange = this.onIdChange.bind(this);
this.editName = this.editName.bind(this);
this.state = {
data: "",
name: "",
surname: "",
id: ""
};
}
componentDidMount() {
axios.get("http://localhost:4000/employees").then((response, err) => {
if (err) {
console.log("err");
}
this.setState(prevstate => ({
data: response.data
}));
});
}
handleSumbit(e) {
axios
.post("http://localhost:4000/employees", {
name: this.state.name,
surname: this.state.surname,
id: this.state.id
})
.then((response, err) => {
if (err) {
console.log("Error While Posting Data", err);
}
console.log("RESPONSE FROM POST", response);
});
}
onNameChange(e) {
this.setState({
name: e.target.value
});
}
onSurnameChange(e) {
this.setState({
surname: e.target.value
});
}
onIdChange(e) {
this.setState({
id: e.target.value
});
}
editName(value) {
this.setState({
name: value
});
}
editSurname(e, value) {
this.setState({
surname: value
});
}
render() {
const { data } = this.state;
return (
<div className="container">
<div>
<label className="">Name</label>
<input
type="text"
name=""
value={this.state.name}
onChange={e => this.onNameChange(e)}
/>
</div>
<div>
<label className="">Surname</label>
<input
type="text"
name=""
value={this.state.surname}
onChange={e => this.onSurnameChange(e)}
/>
</div>
<div>
<label className=""> YOUR ID </label>
<input
type="number"
name=""
value={this.state.id}
onChange={e => this.onIdChange(e)}
/>
</div>
<div>
<button type="button" onClick={e => this.handleSumbit(e)}>
Sumbit
</button>
</div>
<div className="main-container">
{data &&
data.map(function(data, key) {
return (
<React.Fragment>
<div className="child">
{data.name}
<button onClick={e => this.editName("Samar")}>Edit</button>
</div>
<div className="child">
{data.surname}
<button onClick={e => this.editSurname(e, data.surname)}>
Edit
</button>
</div>
</React.Fragment>
);
})}
</div>
</div>
);
}
}
export default App;
I have noticed (as #DanO said) this becomes not a window object but a undefined when using inside map function in render method. The solution is dead simple, either change it to arrow function (preferred) or use Function.prototype.bind.
data.map((data, key) => (<>...</>))