Fill array with key-value values - reactjs

I'm trying to fill an array with objects. If I execute setState, the new entry is not added but the structure of the array is extended. Example: Initial entry (0), new entry (0->0), another entry (0->0->0). I would like however for each entry a running numbering
const [entrys, setEntrys] = useState([{ date: "", entry: "" }]);
->click on button
setEntrys((prev) => [{ ...prev, date: clickedDay, entry: hinweis }]);

The problem you're having is that you have included the previous state, e.g. "prev" with the spread operator inside the object you are passing.
What you want to do is include the new object you are saving to state AND then include previous state, e.g. "prev" as a separate element in the array - like below.
I've included a codesandbox for you too https://codesandbox.io/s/romantic-solomon-l566yy?file=/src/App.js.
import "./styles.css";
import React, { useState } from "react";
const App = () => {
const [entries, setEntries] = useState([
{
date: "",
text: ""
}
]);
const handleClick = () => {
setEntries((prevEntry) => {
return [...prevEntry, { date: "date", text: "test" }];
});
};
return (
<div>
<button onClick={handleClick}>Click me!</button>
</div>
);
};
export default App;

Related

How can we index the keys from nested array of objects to our input set post data?

I'm trying to set course attribute from parameter to my input so that I can be able the insert the value on it but I cannot access the course attribute. ends_level and course becomes any instead of string means I haven't indexed properly the ends_level which the array of object course. I really need your eyes to see something that have missed or missed up. I hope I have explained it well. Thanks in advance y'all.
import { React, useState, useEffect } from 'react';
import Button from 'react-bootstrap/Button';
import { TextField } from '#material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { updateProfile } from '../../../actions/profile';
const Profile = ({ data }) => {
const dispatch = useDispatch();
const currentId = data._id;
const [postData, setPostData] = useState(
{
profile: {
name: "",
},
skills: [
{
end: "",
ends_level: [
{
course: "",
level: ""
}
]
},
],
}
);
const profile = useSelector((state) => currentId ? state.profile.find((p) => p._id === currentId) : null);
useEffect(() => {
if(profile) setPostData(profile);
}, [profile])
const handleSubmit = (e) => {
e.preventDefault();
if(currentId) {
dispatch(updateProfile(currentId, postData));
}
}
return (
<form onSubmit={handleSubmit}>
<TextField
value={postData.profile.name}
onChange={(e) =>
setPostData(
{
...postData, profile:
{
...postData.profile, name: e.target.value
}
}
)
}
/>
<TextField
onChange={(e) =>
setPostData(
{
...postData, skills:
{
...postData.skills, ends_level:
{
...postData.skills.ends_level, course: e.target.value
}
}
}
)
}
/>
<Button variant="primary" size="md" type="submit" block >Save</Button>
</form>
);
}
export default Profile;
The problem seems to be you are using spread operators to spread objects into positions where you originally had arrays. Also when trying to sort out an answer, I feel like you aren't addressing a plurality problem with these arrays. Namely, you have a single target value which may apply to an array full of skills and an array full of ends_levels in each skill, so you have to decide how/what you want the new changed to value to apply to.
Here is a good start, with TypeScript enhancements, that lays out the schema and attempts to craft a new "post data" object which looks like the old one. You will have to fill out the commented section below to affect the array(s) with the new value as you see fit.
interface SkillType {
end: string,
ends_level: [
{
course: string,
level: string
}
]
}
interface DataType {
profile: {
name: string
},
skills: SkillType[]
}
let postData: DataType = {
profile: {
name: "",
},
skills: [
{
end: "",
ends_level: [
{
course: "",
level: ""
}
]
},
],
};
// Create a new holder with all the old skills
let newSkills : SkillType[] = [... postData.skills ];
// Will have multiple skills per post-data
// Each post-data will have multiple "ends_levels" in it
// Do you want to merge it so all of these blocks now have the new value e.target.value ??
// If so, manipulate newSkills
let newPostData: DataType = {
...postData,
skills: newSkills
}

How Refresh Table component onChange Event

I have react component in which I am showing data in the table, also have Select / dropdown. The table and select are in same component. I need to refresh table component soon the value change in the dropdown. My implementation does refresh the table and call API but there is delay when that happened. I am not sure if I have implemented correctly?
the idea is eziSearchCriteria is in useState and onChange event I am assign value to it.
const MyComponent = () => {
const[eziSearchCriteria, setEziSearchCriteria] = useState<IEziStatusSearchCriteriaForm>();
const eziSitesStatusCovers = [
{ label: 'UNSCHEDULED', value: 'UNSCHEDULED' },
{ label: 'COVERED', value: 'COVERED' },
{ label: 'PART COVERED', value: 'PART COVERED' },
];
useEffect(() =>{
setInitialPageLoad(true);
setDefaultSearchCriteria();
},[]);
const handleSearchFilter = (event) =>{
if(event!=null){
eziSearchCriteria.coverStatus = event.value;
setEziSearchCriteria(eziSearchCriteria);
}
}
return (
<div>
<div className="searchFilter">
<Select
options={eziSitesStatusCovers}
onChange = {handleSearchFilter}
/>
</div>
{ eziSearchCriteria ? (
<TableItems
url={url}
apiUrl ={api.eziTrackerStatus}
columns={columns}
customParams= {eziSearchCriteria}
key={eziSearchCriteria.coverStatus}
></TableItems> ) : null}
</div>
);
};
....
export interface IEziStatusSearchCriteriaForm{
startTime: string,
endTime: string,
scheduleId?: number,
coverStatus: string
}
I have found the issue and answer.
I was changing one value in the object eziSearchCriteria but object is same/ reference variable and react state not considering to render on this change. I created new object and assign to eziSearchCriteria and it worked straight away.
const handleSearchFilter = (event) =>{
if(event!=null){
eziSearchCriteria.coverStatus = event.value;
var searchCriteria2 : IEziStatusSearchCriteriaForm = {
startTime: "12-08-2020", //MM:DD:YYYY
endTime: "12-09-2020",
schedAction_Active: "Active",
coverStatus: event.value
}
setEziSearchCriteria(searchCriteria2);
}
}

react - copy state to local variable without changing its value

I have a case that I need to update a value in hierarchy state variable (which consists of 3 tree levels in my case, and I need to update a value in third level for example).
I copied that state variable to local variable and did the update on it, but I noticed that state variable was changed as well!
Searched for it, and saw suggestions for using Object.assign or Spread operator, I tried using the spread but it didn't help with creating a copy of the state to prevent both objects referring to the same reference but it didn't work!
Object.assign appraoch didn't work neither!
Ho can I accomplish that?
const assign = require('object-assign');
// let properties = this.state.configProperties
// let properties = assign({}, this.state.configProperties)
let properties = { ...this.state.configProperties };
properties.Children.forEach((lvl1) => {
lvl1.Properties.forEach((property) => {
if (property.id === id) {
property.value = val;
return;
}
});
lvl1.Children.forEach((lvl2) => {
lvl2.Properties.forEach((property) => {
if (property.id === id) {
property.value = val;
return;
}
});
lvl2.Children.forEach((lvl3) => {
lvl3.Properties.forEach((property) => {
if (property.id === id) {
property.value = val;
return;
}
});
});
});
});
You have to clone the nested level objects/arrays as well, in order to avoid referencing original values.
Here is an example on how to achieve deeply nested objects copying,
import React from "react";
import "./styles.css";
export default function App() {
const state = {
dataOne: {
subDataOne: {
text: "Test",
value: "Test One"
},
subDataTwo: {
text: "Test",
value: "Test One"
}
},
dataTwo: {
subDataOne: {
text: "Test",
value: "Test One"
},
subDataTwo: {
text: "Test",
value: "Test Two"
}
}
};
const clonedState = { ...state };
const clonedData = { ...clonedState.dataOne };
const clonedSubDataOne = { ...clonedData.subDataOne };
clonedSubDataOne.text = "New Test Value";
clonedData.subDataOne = clonedSubDataOne
clonedState.dataOne = clonedData
console.log("Original =>",state)
console.log("Cloned =>",clonedState)
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
You can also use Lodash Library for cloning and many more.
import * as _ from 'lodash';
const copy = _.cloneDeep(object)

Functional component problems React

I transformed a class component into a functional component but it looks like it does not work in a way it suppose to work and I can not find what is wrong. When I create a new object there is no name for the object and when I try to mark the object as a complete it removes all created objects at ones. I created a codesandbox here. Unfortunately, I am not too much familiar with functional component. Any help would be appreciated.
Here is my codesandbox sample:
https://codesandbox.io/s/crazy-sid-09myu?file=/src/App.js
Your Todos:
const [todos, setTodos] = useState([
{ id: uuid(), name: "Task 1", complete: true },
{ id: uuid(), name: "Task 2", complete: false }
]);
onAddHandler:
const addTodo = () =>
setTodos([...todos, { id: uuid(), name: "New Task", complete: false }]);
onSetCompleteHandler:
const setCompleteHandler = id =>
setTodos(
todos.map(todo => {
if (todo.id === id) {
return {
...todo,
complete: todo.complete ? 0 : 1
};
}
return todo;
})
);
I have created your new todos. Check out this link
Todos App
I have updated your code, please check the URL https://codesandbox.io/s/determined-morning-n8lgx?file=/src/App.js
const onComp = id => {
for (let i = 0; i < todos.length; i++) {
if (todos[i].id === id) {
let t = { ...todos[i] };
t.complete = !t.complete;
todos[i] = t;
}
}
setTodos([...todos]); // Here todos reference need to be changed
};
And also
const onSubmit = event => {
event.preventDefault();
setTodos([
...todos,
{
id: generateNewId(),
name: newTodoName,
complete: false
}
]);
setNewTodoName("");
};
While using hooks we need to be careful about state variable updates. While manipulating arrays and objects use the spread operator to create new references which invokes child components and re-render current component.

How to display comments coming from Redux store on individual component

I have created a basic single page app, on initial page there is some dummy data and on click of each item I direct user to individual details page of that item. I wanted to implement comment and delete comment functionality which I successfully did but now when I comment or delete the comment it doesn't only happen at that individual page but in every other page too. Please see the sandbox example for better clarify.
https://codesandbox.io/s/objective-feistel-g62g0?file=/src/components/ProductDetails.js
So once you add some comments in individual page, go back and then click to another products, apparently you will see that the comments you've done in other pages are also available there. What do you think causing this problem ?
The same state being reused by all the different pages.
Try to load dynamically load reducers for each page/router differently to use distinct state values.
You can start from here
Redux modules and code splitting
I found my own logical solution. You probably might find a better solution but this works pretty well too. I thought of passing another property in the object with the params I get from url and then filter the comments by their url params. So that I could do filtering based on the url parameters and display the comments only made on that specific page.
So ProductDetails.js page should be looking like this:
import React, { useState, useEffect } from 'react';
import { Input, Button } from 'semantic-ui-react'
import { connect } from 'react-redux';
const ProductDetails = (props) => {
const [commentObject, setCommentObject] = useState({
text: "",
date: "",
id: ""
});
const clickHandler = () => {
if (!commentObject.text.trim()) {
return
}
props.addNewComment(commentObject)
setCommentObject({
...commentObject,
text: ""
})
console.log(commentObject.id);
}
useEffect(() => {
}, []);
return (
<div>
{props.posts ? props.posts.text : null}
{props.comments.filter(comment => {
return comment.postId === props.match.params.slug
}).map(({ text, id }) => {
return (<div key={id}>
<p>{text}</p>
<Button onClick={() => props.deleteComment(id)} >Delete comment</Button></div>)
})}
<Input value={commentObject.text}
onChange={comment => setCommentObject({ text: comment.target.value, date: new Date(), id: Date.now(), postId: props.match.params.slug })}
/>
<Button onClick={clickHandler} >Add comment</Button>
</div>
);
}
const mapStateToProps = (state, ownProps) => {
let slug = ownProps.match.params.slug;
return {
...state,
posts: state.posts.find(post => post.slug === slug),
}
}
const mapDispatchToProps = (dispatch) => {
return {
addNewComment: (object) => { dispatch({ type: "ADD_COMMENT", payload: { comment: { text: object.text, date: object.date, id: object.id, postId: object.postId } } }) },
deleteComment: (id) => { dispatch({ type: "DELETE_COMMENT", id: id }) }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductDetails);

Resources