I have this function that returns an array of objects, every object represent a sticky, what I want is to change the value of "content" everytime I click one of the stickies
handleStickyEdition = (index) => {
const { currentStage } = this.props
const stickies = currentStage.get('stickies')
const updatedStickies = [...stickies]
console.log(updatedStickies)
}
And the result of calling the console.log is this array of objects:
If I do console.log(updatedStickies[index].get('content')) I will get the content of the object I want to change. For example name 3.
How can I replace this content with an empty string? in other words, if I click the object in the position 0, how can I make name 3 equals to ''
I would suggest using a map like so.
this.setState({
updatedStickies: this.state.updatedStickes.map(sticky => ({
...sticky
content: sticky.id === idOfStickyWeWantToUpdate ? "" : "content"
}))
});
I see you are reading stickies from props, I would suggest having a function in your parent component to run the above code which you can call from your child component if need be.
Related
I want to add another object to my exiting object but i doesn't get the value as others object have instead it gets a text, I tried a easy solution but didn't work, here is that i'm facing:
ConsoleLog Image
So instead of the key long I want to keep the numbers as they started from 0, but I don't know how?
Here is what I've tried:
const addToWatchlist = (movie) => {
const long = movies.length;
const data = {...movies, long: movie};
setMovies(movies)
ps: I'm using React.
const addToWatchlist = (movie) => {
movies.push(movie)
setMovies(movies)
I am still new to React js.
I am trying to use useState({}) to define an object of objects of orders.
For the newOrderHandler, I am passing the order to be added.
The idea is to add a new object if the order title does not exist and update the amount if the order title already exists in the orders state.
This is the code:
const [orders, setOrders] = useState({});
const newOrderHandler = (newOrder) => {
setOrders(prevOrders => {
console.log('prevOrderss', prevOrders)
// console.log(`prevOrders[newOrder.title]`, prevOrders[newOrder.title])
let newOrders = prevOrders;
if (newOrders[newOrder.title] == null) {
newOrders[newOrder.title] = newOrder
} else {
newOrders[newOrder.title].amount = +prevOrders[newOrder.title].amount + +newOrder.amount
}
return newOrders;
});
};
The problem here is that although when I log the prevOrders to the console, I get it as I wanted:
However, when I calculate the number of objects in the Navigation component, it just displays 0 always.
This is the code that calculates the number of objects in the Navigation component:
Your Cart <span>{Object.keys(props.orders).length}</span>
This is how I passed the props to the Navigation component:
<Navigation orders={orders} />
This always displays 0. I guess the problem is when defining this: let newOrders in the setOrders function, but I am not sure how to solve it.
Thanks in advance.
The problem is that you React cannot detect that you have changed the object. You need to make a copy, you are passing in the same reference.
newOrders == prevOrders returns true.
What is standard is to make a copy so that you do not mutate the state and react can detect that the object has actually changed.
You can use the spread operator.
let newOrders = { ...prevOrders, [newOrder.title] : { ...prevOrders[newOrder.title] }};
if (newOrders[newOrder.title] == null) {
newOrders[newOrder.title] = newOrder
} else {
newOrders[newOrder.title].amount = +prevOrders[newOrder.title].amount + +newOrder.amount
}
return newOrders;
Spreading the nested property too because you are mutating its amount property. For every level of nesting you will have to use spread for the property you want to change.
I'm updating an object within react's state which I use to display a list. The state updates correctly, however the display breaks.
This is the section of the code from inside my render function which produces the list.
this.state.shoppingItems ? this.state.shoppingItems.currentShoppingItems.map((item, index) => {
console.log(item)
return <ItemSummary key={index} onClickHandler={this.selectItem} updateShoppingItem={this.updateCurrentShoppingItem} shoppingItem={item} removeFromCurrentItems={this.removeFromCurrentItems} addToCurrentList={this.addToCurrentList} />
}) : undefined}
Here is the code that produces the previous items list:
this.state.shoppingItems ? this.state.shoppingItems.previousShoppingItems.map((item, index) => {
console.log(item)
return <ItemSummary key={index} onClickHandler={this.selectItem} updateShoppingItem={this.updateCurrentShoppingItem} shoppingItem={item} removeFromCurrentItems={this.removeFromCurrentItems} addToCurrentList={this.addToCurrentList} />
}) : undefined}
This is the method which removes the item from the current list and adds it to the previous list, where the issue occurs.
removeFromCurrentItems(shoppingItem) {
const items = this.state.shoppingItems.currentShoppingItems.filter(item => item._id !== shoppingItem._id);
let shoppingItems = this.state.shoppingItems;
shoppingItems.currentShoppingItems = items;
shoppingItem.number = 0;
shoppingItem.completed = false;
shoppingItems.previousShoppingItems.push(shoppingItem);
this.setState({
shoppingItems: shoppingItems
});
// call to API to update in database
}
Here is the list before I remove the item.
Here is the list after I remove the middle item:
Finally here is the console.log output which shows that the items have updated properly but the display hasn't updated:
I'm entirely new to react coming from angular so I have no idea if this is the correct way to do this or if there is a better way. But could somebody help me figure out why the display isn't updating?
The issue seemed to be the key on the item in the map. I replaced the index with the item's id from the database as below and now it renders properly.
return <ItemSummary key={task._id} updateShoppingItem={this.updateCurrentShoppingItem} shoppingItem={task} removeFromCurrentItems={this.removeFromCurrentItems} addToCurrentList={this.addToCurrentList} />
Similar answer here:
Change to React State doesn't update display in functional component
The issue is the update for shoppingItems. You save a reference to the current state object, mutate it, and store it back in state. Spreading this.state.shoppingItems into a new object first will create a new object reference for react to pick up the change of.
React uses shallow object comparison of previous state and prop values to next state and prop values to compute what needs to be rerendered to the DOM and screen.
removeFromCurrentItems(shoppingItem) {
const items = this.state.shoppingItems.currentShoppingItems.filter(item => item._id !== shoppingItem._id);
const shoppingItems = {...this.state.shoppingItems};
shoppingItems.currentShoppingItems = items;
shoppingItem.number = 0;
shoppingItem.completed = false;
shoppingItems.previousShoppingItems.push(shoppingItem);
this.setState({
shoppingItems: shoppingItems
});
// call to API to update in database
}
I had a similar issue with my application in which I had to delete comments made.
<textarea disabled key={note._id} className="form-control">{note.note}</textarea>
But the issue got resolved when I added the Key attribute to the list item.
In my ReactJS application I am getting the mobile numbers as a string which I need to break and generate a link for them to be clickable on the mobile devices. But, instead I am getting [object Object], [object Object] as an output, whereas it should be xxxxx, xxxxx, ....
Also, I need to move this mobileNumbers function to a separate location where it can be accessed via multiple components.
For example: Currently this code is located in the Footer component and this code is also need on the Contact Us component.
...
function isEmpty(value) {
return ((value === undefined) || (value === null))
? ''
: value;
};
function mobileNumbers(value) {
const returning = [];
if(isEmpty(value))
{
var data = value.split(',');
data.map((number, index) => {
var trimed = number.trim();
returning.push(<NavLink to={`tel:${trimed}`} key={index}>{trimed}</NavLink>);
});
return returning.join(', ');
}
return '';
};
...
What am I doing wrong here?
Is there any way to create a separate file for the common constants / functions like this to be accessed when needed?
First question:
What am I doing wrong here?
The issue what you have is happening because of Array.prototype.join(). If creates a string at the end of the day. From the documentation:
The join() method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. If the array has only one item, then that item will be returned without using the separator.
Think about the following:
const navLinks = [{link:'randomlink'}, {link:'randomlink2'}];
console.log(navLinks.join(','))
If you would like to use concatenate with , then you can do similarly like this:
function mobileNumbers(value) {
if(isEmpty(value)) {
const data = value.split(',');
return data.map((number, index) => {
const trimed = number.trim();
return <NavLink to={`tel:${trimed}`} key={index}>{trimed}</NavLink>;
}).reduce((prev, curr) => [prev, ', ', curr]);
}
return [];
};
Then you need to use map() in JSX to make it work.
Second question:
Is there any way to create a separate file for the common constants / functions like this to be accessed when needed?
Usually what I do for constants is that I create in the src folder a file called Consts.js and put there as the following:
export default {
AppLogo: 'assets/logo_large.jpg',
AppTitle: 'Some app name',
RunFunction: function() { console.log(`I'm running`) }
}
Then simply import in a component when something is needed like:
import Consts from './Consts';
And using in render for example:
return <>
<h1>{Consts.AppTitle}</h1>
</>
Similarly you can call functions as well.
+1 suggestion:
Array.prototype.map() returns an array so you don't need to create one as you did earlier. From the documentation:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
I hope this helps!
this.state = {
myArray = [
{
name:"cat",
expand:false
}
]
}
clickItem(item){
item.expand = true;
this.setState({})
}
this.state.myArray.map((item) =>{
return <div onClick={()=>this.clickItem(item)}>{item.name}</div>
})
In React, i have a simple array of objects,
when i click on one of theses object, i want to change their prop and update the state, what is the proper way of doing this.
i feel like there could be a better way
You need to copy your state, update the copied state and the set the state.
this.state = {
myArray = [
{
name:"cat",
expand:false
}
]
}
clickItem(key){
let items = this.state.myArray;
items[key].expand = true;
this.setState({items})
}
this.state.myArray.map((key, item) =>{
return <div onClick={()=>this.clickItem(key)}>{item.name}</div>
})
Okay, a couple of things.
You're mutating the state directly which is going to fail silently and you're also missing the key prop on your <div.
This is easily resolved though by using the data you have available to you. I don't know whether each name is unique but you can use that as your key. This helps React decide which DOM elements to actually update when state changes.
To update your item in state, you need a way to find it within the state originally, so if name is unique, you can use Array.prototype.find to update it.
clickItem(item) {
const targetIndex = this.state.items.find(stateItem => stateItem.name === item.name)
if (targetIndex === -1)
// Handle not finding the element
const target = this.state.items[targetIndex]
target.expand = !target.expand // Toggle instead of setting so double clicking works as expected.
this.setState({
items: this.state.items.splice(targetIndex, 1, target) // This replaces 1 item in the target array with the new one.
})
}
This will update state and re-render your app. The code is untested but it should work.