react localStorage empty array and null on first rendering - reactjs

I try to get used with localStorage and I run into something I'm confused with.
When I try to add the first task at the localStorage it doesn't save the task object however just an empty array and only then after adding the second task, it starts to save the object task ?
Since that my first point somehow isn't working correctly the second issue is that now when I clean up manually the localStorage and refresh the page I run into a null error that occurs.
Here is the code below if someone can give me some insight about it. Thank you
const SearchInput = () => {
const [taskValue, setTaskValue] = useState("");
const [allTasks, setAllTasks] = useState([]);
useEffect(() => {
const persistedTasks = JSON.parse(localStorage.getItem("tasks"));
setAllTasks(persistedTasks);
console.log(persistedTasks);
}, []);
const handleChange = (e) => {
setTaskValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
if (taskValue !== "") {
setAllTasks([
...allTasks,
{ id: allTasks.length + 1, text: taskValue.trim() },
]);
}
localStorage.setItem("tasks", JSON.stringify(allTasks));
setTaskValue("");
};
return (
<>
<Form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a task..."
value={taskValue}
onChange={handleChange}
/>
<button>Submit the Task</button>
</Form>
<TasksContainer>
{!allTasks.length && <p>No tasks</p>}
{!!allTasks.length && (
<ul>
{allTasks.map((task) => (
<TaskWrapper key={task.id}>
<li>{task.text} </li>
<div>
<button>Edit</button>
<button>Delete</button>
</div>
</TaskWrapper>
))}
</ul>
)}
</TasksContainer>
</>
);
};

Related

How to get all input values from a mapped component in React and get all the values in an array?

I need to dynamically generate multiple divs with input-field in it, so the user can add values in it.
By clicking the button user can create as many divs with input-field as he needs
By typing values total amount must be calculated in real-time.
My problem is how to get all input values into one array so I could calculate the total.
I just need any kind of example how to get those values in one array.
*Note I use functional components.
const ReceiptDetails = () => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(null);
const [expenseList, setExpenseList] = useState([]);
const [expenseTotal, setExpenseTotal] = useState(0);
const options = ['Food', 'Entertaiment', 'Houseware'];
const onOptionClicked = (option) => {
setSelectedOption(option);
setIsOpen(false);
};
const formHandler = (e) => {
e.preventDefault();
if (selectedOption !== null) {
setExpenseList(
expenseList.concat(
<ExpenseDetails key={expenseList.length} calcTotal={calcTotal} />
)
);
}
return;
};
return (
<Container>
<Form onSubmit={formHandler}>
<div>
<DropDownHeader onClick={() => setIsOpen(!isOpen)} required>
{selectedOption || 'Select'}
<span className='arrow-head'>⌄</span>
</DropDownHeader>
{isOpen && (
<div>
<DropDownList>
{options.map((option, indx) => (
<ListItem
onClick={() => onOptionClicked(option)}
value={option}
key={indx}
>
{option}
</ListItem>
))}
</DropDownList>
</div>
)}
</div>
<Button type='secondary'>Add expense</Button>
</Form>
{expenseList}
{expenseList.length !== 0 && (
<ExpenseTotal>Total {expenseTotal}</ExpenseTotal>
)}
</Container>
);
};
export default ReceiptDetails;
const ExpenseDetails = () => {
const [expense, setExpense] = useState(0);
return (
<DetailsContainer>
<div className='first'>
<FirstInput type='text' placeholder='Expense name' />
<SecondInput
type='number'
placeholder='€0.00'
value={expense}
onChange={(e) => setExpense(e.target.value)}
/>
</div>
</DetailsContainer>
);
};
export default ExpenseDetails;
how about you use a context? That will allow you to keep and share your state between the components. I'd do something like here. Keep the array of expenses at the context level and the state of the form. From that, you can get the values and display them in the child components.
But it would be best to read the documentation first ;)

ToDo rendering data object

I was just testing around building a dummy todo list and was trying to figure out something. While setting the new state with the new task object that includes an id and a text. Well everything works well just my issue when I console.log(allTasks) it starts only to show the array of data after I have added the second task ?
const SearchInput = () => {
const [taskValue, setTaskValue] = useState("");
const [allTasks, setAllTasks] = useState([]);
const handleChange = (e) => {
setTaskValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
if (taskValue !== "") {
setAllTasks([
...allTasks,
{ id: allTasks.length + 1, text: taskValue.trim() },
]);
}
setTaskValue("");
console.log(allTasks);
};
return (
<>
<Form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a task..."
value={taskValue}
onChange={handleChange}
/>
<button>Submit the Task</button>
</Form>
<div>
{allTasks.length <= 0 ? (
<p>No tasks</p>
) : (
<ul>
{allTasks.map((task) => (
<li key={task.id}> {task.text} </li>
))}
</ul>
)}
</div>
</>
);
};
Here you get updated value and there is a conditional change I hope you will like.
Thanks
const SearchInput = () => {
const [taskValue, setTaskValue] = React.useState("");
const [allTasks, setAllTasks] = React.useState([]);
const handleChange = (e) => {
setTaskValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
if (taskValue !== "") {
setAllTasks([
...allTasks,
{ id: allTasks.length + 1, text: taskValue.trim() },
]);
}
setTaskValue("");
};
console.log(allTasks);
return (
<>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Add a task..."
value={taskValue}
onChange={handleChange}
/>
<button>Submit the Task</button>
</form>
<div>
{!allTasks.length && <p>No tasks</p>}
{!!allTasks.length &&
<ul>
{allTasks.map((task) => (
<li key={task.id}> {task.text} </li>
))}
</ul>
}
</div>
</>
);
};
According to the docs, setState is async in nature, which means it will execute only after execution of all synchronous code. And setState takes a callback as the second parameter which you can use to log it as expected.
setAllTasks([
...allTasks,
{ id: allTasks.length + 1, text: taskValue.trim() },
], ()=>{
console.log(alltasks)
});
Reference

All other input data get clear when I remove an input

Hi i am working on a React application where there are four inputs.when a user add an input the element will be added to the wrapper.In the following code add operation works fine but remove operation is not working properly ,it is not removing the corresponding element.Another problem the values on the inputs fields not present when the component re-renders.so experts guide me how i can achieve removing the corresponding row when the remove button is clicked and the input values should not be reset when the component re-renders
So when I refresh the page and click to remove an input it will clear all other input data. How can I fix this problem ?
Update added full component to question:
const Agreement = (props) => {
const { agreement, editable, teamData, teamId, fetchTeamData } = props;
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [showErrors, setShowErrors] = useState(false);
const [errorsArr, setErrorsArr] = useState();
const initialFormState = {
rule_0: teamData.rule_0,
rule_1: teamData.rule_1,
rule_2: teamData.rule_2,
rule_3: teamData.rule_3,
creator: teamData.User.public_user_id,
};
const [updateTeamData, setUpdateTeamData] = useState(initialFormState);
const [inputs, setInputs] = useState(teamData.rules);
const handleChange = (event) => {
const { name, value } = event.target;
// Update state
setUpdateTeamData((prevState) => ({
...prevState,
[name]: value,
}));
};
// Add more input
const addInputs = () => {
setInputs([...inputs, { name: `rule-${inputs.length + 1}` }]);
};
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...inputs];
list.splice(index, 1);
setInputs(list);
};
const clearInput = (dataName) => {
setUpdateTeamData((prevState) => {
delete prevState[dataName];
return {
...prevState,
};
});
};
const handleSubmit = async (event) => {
event.preventDefault();
setEditing(false);
// Send update request
console.log(updateTeamData);
const res = await axios.put(`/api/v1/teams/team/${teamId}`, updateTeamData);
// If no validation errors were found
// Validation errors don't throw errors, it returns an array to display.
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Call update profiles on parent
fetchTeamData();
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
const handleCancel = () => {
setEditing(false);
};
useEffect(() => {
if (agreement === "default") {
setTitle(defaultTitle);
setInputs(teamData.rules);
} else {
setTitle(agreement.title ?? "");
}
}, [agreement, teamData]);
return (
<div className="team-agreement-container">
{!editing && (
<>
<h4 className="team-agreement-rules-title">{title}</h4>
{editable && (
<div className="team-agreement-rules">
<EditOutlined
className="team-agreement-rules-edit-icon"
onClick={() => setEditing(true)}
/>
</div>
)}
<p className="team-agreement-rules-description">{description}</p>
{teamData.rules.map((rule, index) => (
<div className="team-agreement-rule-item" key={`rule-${index}`}>
{rule ? (
<div>
<h4 className="team-agreement-rule-item-title">
{`Rule #${index + 1}`}
</h4>
<p className="team-agreement-rule-item-description">
- {rule}
</p>
</div>
) : (
""
)}
</div>
))}
</>
)}
{/* Edit rules form */}
{editing && (
<div className="team-agreement-form">
{showErrors && <ModalErrorHandler errorsArr={errorsArr} />}
<h1>Rules</h1>
{inputs.map((data, idx) => {
return (
<div className="agreement-form-grid" key={`${data}-${idx}`}>
<button
type="button"
className="agreement-remove-button"
onClick={() => {
handleRemoveClick(idx);
clearInput(`rule_${idx}`);
}}
>
<Remove />
</button>
<input
type="text"
placeholder={`Rule ${idx + 1}`}
defaultValue={teamData.rules[idx]}
name={`rule_${idx}`}
onChange={handleChange}
/>
</div>
);
})}
{inputs.length < 4 && (
<div className="team-agreement-add-rule">
<button type="submit" onClick={addInputs}>
<Add />
</button>
</div>
)}
<div className="div-button">
<button className="save-button" onClick={handleSubmit}>
Save
</button>
<button className="cancel-button" onClick={handleCancel}>
Cancel
</button>
</div>
</div>
)}
</div>
);
};
export default Agreement;

All other input data get clear when I i hit handleSubmit

Hi i am working on a React application where there are four options.when a user select an option corresponding input element will be added to the wrapper.In the following code add operation works fine but remove operation is not working properly ,it is not removing the corresponding element.Another problem the values on the inputs fields not present when the component re-renders.so experts guide me how i can acheive removing the corresponding row when the remove button is clicked and the input values should not be reset when the component re-renders.
But when I submit the input it will appear my data perfectly and when i restart the page and just click into edit and hit submit with the defaultValue it just clear all the data and send back to my backend with undefined value like this: [ undefined, undefined, undefined, undefined ]
Here is my full component:
const Agreement = (props) => {
const { agreement, editable, teamData, teamId, fetchTeamData } = props;
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [showErrors, setShowErrors] = useState(false);
const [errorsArr, setErrorsArr] = useState();
const initialFormState = {
rule_0: teamData.rules.rule_0,
rule_1: teamData.rules.rule_1,
rule_2: teamData.rules.rule_2,
rule_3: teamData.rules.rule_3,
creator: teamData.User.public_user_id,
};
const [updateTeamData, setUpdateTeamData] = useState(initialFormState);
const [inputs, setInputs] = useState(teamData.rules);
const handleChange = (event) => {
const { name, value } = event.target;
// Update state
setUpdateTeamData((prevState) => ({
...prevState,
[name]: value,
}));
};
// Add more input
const addInputs = () => {
setInputs([...inputs, { name: `rule_${inputs.length + 1}` }]);
};
// handle click event of the Remove button
const removeInputs = (index) => {
const list = [...inputs];
list.splice(index, 1);
setInputs(list);
};
const clearInput = (dataName) => {
setUpdateTeamData((prevState) => {
delete prevState[dataName];
return {
...prevState,
};
});
};
const handleSubmit = async (event) => {
event.preventDefault();
setEditing(false);
// Send update request
const res = await axios.put(`/api/v1/teams/team/${teamId}`, updateTeamData);
// If no validation errors were found
// Validation errors don't throw errors, it returns an array to display.
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Call update profiles on parent
fetchTeamData();
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
const handleCancel = () => {
setEditing(false);
};
useEffect(() => {
if (agreement === "default") {
setTitle(defaultTitle);
setInputs(teamData.rules);
} else {
setTitle(agreement.title ?? "");
}
}, [agreement, teamData]);
console.log("teamData.rules", teamData.rules);
console.log("inputs", inputs);
return (
<div className="team-agreement-container">
{!editing && (
<>
<h4 className="team-agreement-rules-title">{title}</h4>
{editable && (
<div className="team-agreement-rules">
<EditOutlined
className="team-agreement-rules-edit-icon"
onClick={() => setEditing(true)}
/>
</div>
)}
<p className="team-agreement-rules-description">{description}</p>
{teamData.rules.map((rule, index) => (
<div className="team-agreement-rule-item" key={`rule-${index}`}>
{rule ? (
<div>
<h4 className="team-agreement-rule-item-title">
{`Rule #${index + 1}`}
</h4>
<p className="team-agreement-rule-item-description">
- {rule}
</p>
</div>
) : (
""
)}
</div>
))}
</>
)}
{/* Edit rules form */}
{editing && (
<div className="team-agreement-form">
{showErrors && <ModalErrorHandler errorsArr={errorsArr} />}
<h1>Rules</h1>
{inputs.map((data, idx) => {
return (
<div className="agreement-form-grid" key={`${data}-${idx}`}>
<button
type="button"
className="agreement-remove-button"
onClick={() => {
removeInputs(idx);
clearInput(`rule_${idx}`);
}}
>
<Remove />
</button>
<input
name={`rule_${idx}`}
onChange={handleChange}
value={teamData.rules[idx]}
/>
</div>
);
})}
{inputs.length < 4 && (
<div className="team-agreement-add-rule">
<button type="submit" onClick={addInputs}>
<Add />
</button>
</div>
)}
<div className="div-button">
<button className="save-button" onClick={handleSubmit}>
Save
</button>
<button className="cancel-button" onClick={handleCancel}>
Cancel
</button>
</div>
</div>
)}
</div>
);
};
export default Agreement;
How can I fix this error?
My thought is the problem is around [inputs, setInputs]
Try this
<input
//..
onChange={(event) => handleChange(event.target.value)}
//..
/>
then in your "handleChange" function
const handleChange = (event) => {
const { name, value } = event;
//....
};

I don`t understand how to render objects(react)

I am getting a object from "api" and set it to match but when i try do loop through or render it i get a error.
I have tryed Objct.keys maybe my syntax is wrong im not sure im still learning thx for any help.
const [match, setMatch] = useState();
const [search, setSearch] = useState('');
const [query, setQuery] = useState(4749875544)
useEffect(() => {
getData();
}, [query]);
const getData = async () => {
const response = await
fetch(`https://api.opendota.com/api/matches/${query}`)
const result = await response.json();
setMatch(result);
}
}
return (
<div className="App" >
<form onSubmit={getSearch}
className="search-form">
<input className="search-bar"
type="text"
value={search}
onChange={searchInput}
/>
<Button as="input"
type="submit"
value="Submit" />
</form>
<li>
{
Object.keys(match).map((oneKey,i)=>{
return (
<li key={i}>{match[oneKey]}</li>
)})
}
</li>
</div>
)}
First I would default the state to an Object. It is always good to default your state to the data types you will use. So at the top useState({}).
React can’t render an object. You have to render each key separately. In your map when you return the list item do it with match[oneKey].title or whatever key is actially valid.

Resources