Changing an array of objects with React useState - javascript

I have a calendar app that renders its events with an array of objects:
const [events, setEvents] = useState([
{
title: "Werk",
start: moment().toDate(),
end: moment(),
allDay: false,
},
])
I am trying to allow the user to add new events with the inputs on the page.
So far I am just trying to change the title of the event with this code
<input
placeholder="Name"
onChange={(e) =>
setEvents([{ ...events, title: e.target.value }, console.log(events.title)])
}
required
></input>
When I console.log(e.target.value) it gives me what I am typing in, but the (events.title) is giving me undefined.
My end goal is to have the user add new events with the inputs in the following format. If I'm on the wrong track, or you know a better way I'd love to hear it.
const [events, setEvents] = useState([
{
title: "Werk",
start: moment().toDate(),
end: moment(),
allDay: false,
},
// this is what the user will add
{
title: "Example",
start: Number,
end: Number
}
])
Thank you in advance, my full code is Here And here is a codesandbox: https://codesandbox.io/embed/gracious-minsky-pp1w5?codemirror=1

I think that that console.log is missplaced, you shouldn't be calling that function inside an array assignment. And even if you call it below the setEvents function will not give you the title because the set function of the useState is asynchronous. Also I think you are not doing right the assignment of the events into your array.
In this line:
setEvents([{ ...events, title: e.target.value }, console.log(events.title)])
You are spreading the events inside a new array and then adding the title, leaving you with a structure similar to this:
[{
"0": {
"title": "Werk",
"start": "2020-12-05T05:12:57.931Z",
"end": "2020-12-05T05:12:57.931Z",
"allDay": false
},
"title": "myNewTitle"
}]
And nesting this titles for every change in the input.
For the structure you want to achieve you must push a new entry into an array that already contains your previous events, and then update the state. Like:
//I think the best approach is calling this in a submit or button click event
const buttonClick = () => {
const newEvents = [...events]; //This spreads every element inside the events array into a new one
newEvents.push({
title: eventTitleState, //You also need a useState for the title
start: moment().toDate(),
end: moment(),
});
setEvents(newEvents);
}
You can see this idea working here: https://codesandbox.io/s/green-cherry-xuon7

Your method of changing title is incorrect. Since it is array of objects, you will need to get to the specific object and then change the particular title. In your case replacing this code with yours helps in changing title of 0 index.
<input
placeholder="Name"
onChange={(e) =>{
let tempArr = [...events];
tempArr[0].title = e.target.value;
setEvents(tempArr)
}}
required
>
If you also want to add new objects, push it in tempArr on some event and update setEvents.

Related

How to use spread operator in setstate react class component

I am developing a component where I will get the data from a call back function. Initially the state of the component will be empty [], later once the callback function is called I need to update the values into the state. At a time I'll recive only one array, meaning user can add one item at a time that item will consists of nested objects and array values. I have added the logic for the same to handle the scenario, but when I am testing in jest when I am trying to add another set of item from mock meaning the user can select next item when the done with selecting and submitting the first item at that time my logic is getting failed, I am not getting where I went wrong, could any one help me to resolve this issue, thanks in advance! I have added the mock data structure and logic and jest test below.
Mock:
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
}
ItemID:'123'
}
Code:
class ItemComp extends React.Component{
this.state = {
processingItems:[]
onAddItemHandle = (processingItem) => {
this.setState(prevState => ({
processingItems: [...prevState.processingItems, processingItem]
}))
}
JEST:
describe('handleonAddItem', () => {
it('should allow to add multiple items based on prevState', () => {
const compView = mountWithIntl(
<compView
itemId={12}
/>
}
const instance = compView.find(compViewComponent).instance();
instance.onAddItemHandle(items) // when I am giving only one instance my logic is working
instance.onAddItemHandle(items) //when I am giving it for second time it's failing I am getting error like expected - 0 , received +18 I want to update the items here when user clicks for second time but it is failing.
expect(instance.state.processingItems).toEqual([items])
Missing a ',' before the ItemID is the only issue I faced while reproducing.- https://codesandbox.io/s/intelligent-chaplygin-0ot56e?file=/src/App.js
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
},
ItemID:'123'
}

create a div with elements if it is nested under a specific style

I can't figure why the order of my replaceChild fires off before the condition is met. I want to replace a specific child element when it gets moved to a different container with another element suited for that container. This is happening inside a function that checks to see which buttons are clicked. When the check mark button is clicked. It will move the entire list item from a div called tasklist to a div called completed. Any help is appreciated. Here is my code:
function statusCheck(e) {
const item = e.target;
if (item.classList.contains('trash-btn')) {
const task = item.parentElement.parentElement;
task.classList.add('vanish');
task.addEventListener('transitionend', function(){
task.remove();
});
}
if (item.classList.contains('check-btn')) {
let task = item.parentElement.parentElement;
task.classList.add('complete');
item.remove();
completed.appendChild(task);
task = document.querySelector('.complete');
const taskItem = document.querySelector('.card-date');
if (task.classList.contains('complete')) {
console.log(taskItem);
const swapTask = taskItem.children[0];
console.log(swapTask);
const swapTaskItem = document.createElement('span');
swapTaskItem.classList.add('text-complete');
swapTaskItem.innerText = 'Completed: ';
taskItem.replaceChild(swapTaskItem, swapTask);
}
}
This picture shows Completed before moving to my completed div. Sometimes they stack in the Done column with a 'Completed: Completed:'
I'd suggest a different direction. Using the DOM to track application state isn't a great strategy. I would keep a state object for your tasks and refer to that instead. Data-driven DOM rather than vice-versa. This is, in general terms, what front-end app libraries like React, Angular, and Vue do for you. You can approximate that functionality with something like this:
let items = [{
id: 1,
assignedTo: '',
description: '',
dueDate: '',
completed: false
}, {
id: 2,
assignedTo: '',
description: '',
dueDate: '',
completed: false
}, {
id: 3,
assignedTo: '',
description: '',
dueDate: '',
completed: false
}];
Then, rather than moving elements around and updating classes, you'd update properties in each task object as needed, and you'd refresh your view by re-mapping them:
items.map(item => {
// put items into the page as appropriate
});

React json list

I hope I can explain the question clearly. I need to create a few categories in category with <input type="checkbox"/>. Can you help how do it
{
"properties":{
"category":[
"0" : "category-1",
"1" : "category-2"
],
"image": "https://link...",
...
}
}
now I can only add one value at a time, but I need a lot
{
"properties":{
"category": "category-1",
"image": "https://link...",
...
}
}
const handleSubmit = (e) =>{
e.preventDefault();
const movie = {id, category, title, poster, rating, year, video, trailer, description};
fetch('http://localhost:8000/movies', {
method: 'POST',
headers: {"Content-Type" : "application/json" },
body: JSON.stringify(movie)
}).then(() => {
alert('Успешно добавился фильм!');
})
}
So if im understanding you correctly, you want to add multiple categories to the body of a POST endpoint? As long as your api handles an array for the “category”, then its pretty easy! But first, there are some things I see in your code:
In javascript, you don’t explicitly write the index of an array. You just write the item, for
example

const listOfStrings = [“string 1”, “string 2”]
const listOfObj = [{name: ‘Aj”}, {name: ‘bj’}]
It seems like you are implicitly passing in the properties for your “movie” object, just make sure that each property is defined somewhere above.
Now on to your question! What you want to do is the following:
Create a list of categories, make sure they match whatever the backend is expecting
create a state to track the selected categories
map through the array and render an input for each
assign each input props based on its indexed category
create a function that updates the selected state with a new category or a category removed
The key principles you'll need to research if youre unfamiliar with this are:
Javascript: map, filter
React: JSX,
Rendering lists
hooks (useState)
Code Example: https://codesandbox.io/s/jolly-moon-fbhs7c?file=/src/App.js

Click on React list of elements and store it as a value in another list

I have this sample code which is a search input that filters and returns different colors in a list. When you click on one of the items the innerHTML from that list item then populates the value in the input field.
My question is when I click on one of the list items, I would want to get that specific list item and store it in another list. Much like a search history. Is this difficult to add to the current state of the search field? How would you suggest that I'd approach this?
Do i need to have some kind of onSubmit function to achieve this?
See this example:
https://codesandbox.io/s/autocomplete-6lkgu
Thanks beforehand,
Erik
You can create a state variable searchHistory, which can be array, and onClick of the item, you can do :
onClick = e => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [...this.state.searchHistory, e.currentTarget.innerText],
});
};
I'm assuming you want all of the search items to be unique? You can use a set for that. Here is the code below for your AutoComplete component.
constructor
constructor(props) {
super(props);
this.state = {
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: "",
searchHistory: new Set([])
};
}
onClick
onClick = e => {
const { searchHistory } = this.state;
searchHistory.add(e.currentTarget.innerText);
this.setState(
{
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [
...searchHistory,
searchHistory
]
},
() => {
console.log("[search history]", this.state.searchHistory);
}
);
};
You can simply create an array in your state and push each option into it inside your onClick handler. I made a simple example here that just logs the history when it changes. You can then use/display it any way you'd like.
Note that you'd also need to add similar functionality inside of your onKeyDown handler, as it appears you're also triggering a change with the enter key. I updated the fork to include this.

Javascript. adding items to an array updates all items

This question is somewhat related to this issue I had earlier today:
Adding items to an array in javascript
It works to add items to my array now, but it seems that when I update the array all items will be the same even though the object passed into the method is different everytime
My method looks like this:
addShoe(shoe) {
console.log("Adding new shoe to collection: ");
console.log(shoe);
this.setState(
{
shoes: [...this.state.shoes, shoe]
},
function() {
console.log("Shoe collection:");
console.log(this.state.shoes);
}
);
}
So after one run, this is what the console in Chrome looks like. Which seems to be right:
When I try to add one more to the collection, this is what happens:
Now my collection contains two items which is correct, but it seems like all items in the collection has the same data?
What am I doing wrong here?
EDIT
In another React component I have the following state:
this.state = {
shoe: {
selectedBrand: "",
selectedEU: "",
selectedUS: "",
selectedUK: "",
selectedFraction: ""
}
};
Once a field is updated with a new value, the following method will be triggered:
updateSelectedValues(property, event) {
const shoe = this.state.shoe;
shoe[property] = event.value;
this.setState({ shoe: shoe });
}
When a button in this modal window is closed, the this.state.shoe will be pass as a param to method in the "parent" component.

Categories