I have such situation:
There is an array:
const arr = [
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
]
Then I use this array in my React component to get values, but here I also need to change status to true or false in current element (not for all objects)
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => !status}>{div.title}</div> // here
</div>
)
})
So how can I make this?
How about simply (if you have the access to the arr variable) you have an option of getting an index of the array as the 2nd parameter in the map function.
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => {
arr[ix].status = !arr[ix].status
}}>{div.title}</div> // here
</div>
)
})
or pull it out in a function (better way) as
function handleClick (ix) {
const currentStatus = arr[ix].status
arr[ix].status = !currentStatus
}
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => handleClick(ix)}>{div.title}</div> // here
</div>
)
})
EDIT: Didn't see it was a reactJS, my bad, in that case the best case is to manage it with state.
You can try:
const handleClick = (el) => () => {
el.status = !el.status
}
arr.map(el => {
return (
<div key={el.id}>
<div onClick={handleClick(el)}>{div.title}</div> // here
</div>
)
})
Maintain array in react state;
const [arr,setArr]=useState([
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
])
Write a function to handle the change in state
const changeStatus =id=>{
const newArr = arr.map((el,index)=>{
if(el.id===id){
return {...el,status:!el.status}
}
else return;
})
setArr(newArr)
}
call this function on onClick.
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => changeStatus(el.id)}>{div.title}</div> // here
</div>
)
})
Related
i need your help please :-)
state = {
order: {
createdAt: Date.now(),
products: [
{
leftInStock: "12",
productName: "בלה",
productPrice: "120",
qtyInOrder: 0
}
],
totalPrice: 0,
miniClient: {
id: '',
fullName: ''
}
},
client: null,
isClientSelected: true
}
and this is the function that should preform the update on the object (need to update qtyInOrder).
setQtyForProduct = (product, name) => {
if (name === '+') product.qtyInOrder++
if (name === '-') {
if (product.qtyInOrder === 1) return
else product.qtyInOrder--
}
const { products } = this.state.order
this.setState({ order: { ...this.state.order, products: [...products, product] } }, () => { this.getOrderPrice() })
}
this is how i pass the values
<div className="order-selected-product-list">
{this.state.order.products.map((product, _id) => {
return (
<div key={_id} className="order-product">
<div className="order-selected-product-card">
<div>{product.productName}</div>
<div>{product.qtyInOrder}</div>
</div>
<div className="order-products-qty-btns">
<div onClick={() => this.setQtyForProduct(product, "+")}>+</div>
<div onClick={() => this.removeProductFromOrder(product)}>הסר</div>
<div onClick={() => this.setQtyForProduct(product, "-")}>-</div>
</div>
</div>
)
})}
</div>
i do get the values right , the only thing i caould not manage to do is update the
I know that is something wrong with my setState function cause it adds the products instead of updating the key in it
How to add a class name in every row without effect the rest of the rows
import React, { useState } from 'react';
import './testEfect.css';
const Test = () => {
const arrayTest = [
{
name: '11',
id: '11'
},
{
name: '22',
id: '22'
},
{
name: '33',
id: '33'
},
]
const [state, setState] = useState(false);
const handleClick = (event) => {
const newState = event;
setState(state ? false : true);
}
return (
<div className="App">
{arrayTest.map((x, index) => {
return (
<ul key={index} className={state ? 'deletEfect' : ''}>
<li id={x.id} >
{x.name}
<button onClick={(event) => handleClick(x.id)}>Delete</button>
</li>
</ul>
)
})}
</div>
)
}
The problem here is that when you say the state is false; it is assuming the state is false for the whole component. It doesn't update the row but the whole component. So, at first, you need to add a deleted property that will take a different value for each row.
So,
const arrayTest = [
{
name: "11",
id: "11",
deleted: false
},
{
name: "22",
id: "22",
deleted: false
},
{
name: "33",
id: "33",
deleted: false
}
];
const [state, setState] = useState(arrayTest); //initial state
Now, when you render, you don't need to use that arrayTest. But you need to use the state. We won't touch arrayTest ever again. So we use,
{state.map((x, index) => {
return (
<ul key={index} className={x.deleted ? "testEfect" : ""}>
<li id={x.id}>
{x.name}
<button onClick={(event) => handleClick(x.id)}>Delete</button>
</li>
</ul>
);
})}
Notice we use state.map. We also send x.id to handleClick function.
Why? Because we will use that id to change the deleted value of the object. So our handleClick becomes,
const handleClick = (id) => {
const newState = state.map((element) => {
if (element.id === id)
return Object.assign({}, element, {
deleted: element.deleted ? false : true
});
return element;
});
setState(newState);
};
This is just updating the state in an immutable way.
Here is the full codesandbox for your convenience.
I have a very simple example about useTransition, my expectation is whenever i click on the shuffle button, the items below swap around by a smooth animation. But i doesn't work, the item does swapping but also the pos property. I think my understand about key in useTransition has something wrong, but i can't find it.
my current code: https://codesandbox.io/s/wonderful-solomon-c0sve?file=/src/index.jsx
what im trying to do is something like this
function App() {
const [items, setState] = useState([
{ name: 'C' },
{ name: 'D' },
{ name: 'E' },
{ name: 'F' },
{ name: 'G' },
{ name: 'A' },
{ name: 'B' },
]);
let index = -1;
const gridItems = items.map((item) => {
index += 1;
return { ...item, pos: index * 60 };
});
const transitions = useTransition(gridItems, item => item.name, {
from: () => ({ pos: -100 }),
enter: ({ pos }) => ({ pos }),
udpate: ({ pos }) => ({ pos }),
leave: () => ({ pos: -100 }),
});
return (
<div>
This is app<br/>
<button onClick={ () => setState(Lodash.shuffle) }>shuffle</button><br/><br/>
<div>
{transitions.map(({ item, props, key }) => {
return (
<animated.div
key={key}
className="item"
style={{ transform: props.pos.interpolate(pos => `translateY(${pos}px)`) }}
>
{`${item.name}`}
</animated.div>
)
})}
</div>
</div>
)
}
It was an age to figuring it out. You made a typo.
Try with this one:
update: ({ pos }) => ({ pos }),
Problem: I have a state. And I want to reach the value in this state. But the value in the array.
state = {
base: [ {tomato: false}, {egg: false} ],
contents: [
{mushroom: false},
{olive: false},
{greenPepper: false},
{sausage: false},
{tomato: false},
{redPapper: false}
]
};
class Preivew extends React.Component{
state = {
base: this.props.contents.base
};
getBase = () => {
return(
this.state.base.map( (value) => {
console.log(value)
} )
)
}
render(){
return(
<Wrap>
<div className={classes.imageBox}>
<div className={classes.image}>
{console.log(this.getBase())}
</div>
</div>
</Wrap>
)
}
};
output:
{tomato: false}
{egg: false}
I want to reach "false" here. Because I'm going to do check operations with "if else".
Use Object.values.
getBase = () => this.state.base.map(value => Object.values(value)[0])
You can get the first element of Object.values()
getBase = () => {
return(
this.state.base.map( (value) => {
console.log(Object.values(value)[0])
} )
)
}
this.state.base.map( (value) => {
Object.keys(value).forEach(key => {
console.log(value[key])
})
}
You can solve this using Object.keys in combination with forEach.
Update this property
getBase = (ingredient) => {
const ingPresent = this.state.base.find(value => value[ingredient] != undefined);
return ingPresent ? ingPresent[ingredient] : false;
};
And in the render
render(){
{this.getBase('tomato') && (
<p>Hello tomato present</p>
)}
}
The function also handles the case when the base array doesn't contain the ingredient
Please help me, I'm new in react. I'm rendering values from nested object. Each object has title and message property. Titles can be same. I want display messages under title. If title same as in previos object do not display it , only once. But in my case it displays after each message.
my object:
arrayOfMessages=[
{
title: 'cars',
message: 'toyota'
},
{
title: 'cars',
message: 'ford'
},
{
title: 'cars',
message: 'bmw'
},
{
title: 'bikes',
message: 'suzuki'
},
{
title: 'bikes',
message: 'bmw'
},
]
expected output:
title
message
message
message
title2
message
message
in my case:
title
message
title
message
title
message
title2
message
title2
message
<div>
{arrayOfMessages.map((item, idx) => {
const {
message,
title
} = item
return (
<div key={idx} className="message-content">
<p>{title}</p>
<p>{message}</p>
</div>
)
})}
</div>
One approach would be to group items of arrayOfMessages by the title field, to achieve the required rendering result by using the native .reduce() method:
const arrayOfMessages=[
{
title: 'cars',
message: 'toyota'
},
{
title: 'cars',
message: 'ford'
},
{
title: 'cars',
message: 'bmw'
},
{
title: 'bikes',
message: 'suzuki'
},
{
title: 'bikes',
message: 'bmw'
},
];
/* Use reduce() to group items of arrayOfMessages by title */
const groupedMessages = arrayOfMessages.reduce((groups, item) => {
/* Find group for the title of current item */
const group = groups.find(group => group.title === item.title);
/* If matching group found, add message of item to it's messages array */
if(group) {
group.messages.push(item.message);
}
/* Otherwise, add a new group for this title */
else {
groups.push({ title : item.title, messages : [] });
}
return groups;
}, [])
console.log(groupedMessages);
Using the code above, you could then revise your render() method to render the title once for each item category:
<div>
{ groupedMessages.map((group, idx0) => (<div key={idx0} className="message-content">
<h2>{ group.title }</h2>
{ group.messages.map((message, idx1) => (<p key={idx1}>{message}</p>)) }
</div>))
}
</div>
Hope that helps!
Your object is not well structured, should be like this:
arrayOfMessages=[
{
title:"cars",
description:["toyota","ford","bmw"]
},
{
title:"bike",
description:["suzuki","bmw"]
}
]
then you can implement your code like this :
{
arrayOfMessages.map((message, idx) => {
const { description, title } = message
return (
<div key={idx} className="message-content">
<p>{title}</p>
{description.map((desc, index) => (
<p key={index}>desc</p>
))}
</div>
)
})
}
You should make up your data first.
...
let renderMessages = {}
arrayOfMessages.forEach(message => {
if (!renderMessages[message.title]) {
renderMessages[message.title] = {
title: message.title,
messages: [message.message]
}
} else {
renderMessages[message.title].messages.push(message.message)
}
})
return (
<div>
{Object.keys(renderMessages).map(key => {
let msg = renderMessages[key]
return <div key={key} className="message-content">
<p>{msg.title}</p>
{msg.messages.map(content => <p key={content}>{content}</p>)}
</div>
})}
</div>
)
You could group the messages based on the title using reduce. Then loop through the entries of the merged object to get the desired format
const arrayOfMessages=[{title:'cars',message:'toyota'},{title:'cars',message:'ford'},{title:'cars',message:'bmw'},{title:'bikes',message:'suzuki'},{title:'bikes',message:'bmw'},]
const grouped = arrayOfMessages.reduce((acc, { title, message }) => {
acc[title] = acc[title] || [];
acc[title].push(message);
return acc;
}, {})
console.log(grouped)
Here's a live demo:
class Sample extends React.Component {
render() {
const arrayOfMessages = [{ title: 'cars', message: 'toyota' }, { title: 'cars', message: 'ford' }, { title: 'cars', message: 'bmw' }, { title: 'bikes', message: 'suzuki' }, { title: 'bikes', message: 'bmw' },]
const grouped = arrayOfMessages.reduce((acc, { title, message }) => {
acc[title] = acc[title] || [];
acc[title].push(message);
return acc;
}, {})
return (
<div>
{
Object.entries(grouped).map(([title, messages], i1) => (
<div key={i1}>
<h1>{title}</h1>
{ messages.map((message, i2) => (<p key={i2}>{message}</p>)) }
</div>)
)
}
</div>
);
}
}
// Render it
ReactDOM.render(
<Sample />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
try this
render(){
//title watcher
let watcher = {};
return <div>
{arrayOfMessages.map((item, idx) => {
const {
message,
title
} = item
if(!watcher[title]){
//update watcher with title
//if watcher does not contain your title return title and message
watcher[title]=title;
return (
<div key={idx} className="message-content">
<p>{title}</p>
<p>{message}</p>
</div>
)
} else {
//if watcher found with title return your message
<div key={idx} className="message-content">
<p>{message}</p>
</div>
}
})}
</div>
}