Delete an object inside an object reactjs redux? - reactjs

I am building a simple application with reactjs, redux; The database like this
const initialState = [
{
title: "List 1",
id: 0,
cards: [
{
id: 0,
text: "Task 1",
},
{
id: 1,
text: "Task 2",
},
],
},
{
title: "List 2",
id: 1,
cards: [
{
id: 0,
text: "Task 3",
},
{
id: 1,
text: "Task 4",
},
],
},
];
The app has many lists. In many list has many cards, I want delete a card in a list
So in my listReducer.js. I created a delete reducer to delete a task like this.
case CONSTANTS.DELETE_CARD: {
const { listId, id } = action.payload;
return state.map((list) => {
if (list.id === listId) {
return {
...list,
cards: list.cards.filter((card) => card.id !== id),
};
}
return list;
});
}
But It not working correctly. What I am doing wrong?
the code: https://codesandbox.io/s/github/htactive-nhaptt/crud-trello?file=/src/reducers/listReducers.js:1466-1773

The issue is not with your reducer. I looked at your codesandbox example and added a few console.log and looks like everything gets updated correctly.
The problem is in the render function of your List component in components/List.js. Because you're not passing a unique key to Card component, React doesn't know what items have changed and it only sees a change in cards list lenght. So on re-render, it renders the old list up to the new length (hope it makes sense!) See here for more info on keys: https://reactjs.org/docs/lists-and-keys.html#keys.
You can fix the issue by passing a unique key to Card like this on line 72:
return <Card key={card.id} id={card.id} listId={listId} text={card.text} />;

Related

update an object with useState hook in React

I have an object in board variable
Initial Data:
const [board, setBoard] = useState({
lanes: [],
});
{
lanes: [{
title: 'Bugs',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd03',
cards: [{
id: null,
title: 'Bug #1',
description: 'Can AI make memes',
}, ],
},
{
title: 'Tests',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd04',
cards: [{
id: null,
title: 'Test #1',
description: 'Can AI make memes',
}, ],
},
],
};
I want to add a new element to the cards array but only to the first element in the lanes array. Other answers seem to point to having to use a callback pattern, but I am quite unfamiliar with this.
Thanks for any help.
As for any modification you want to do on a useState variable, you must use an arrow function inside of the "set" function.
You can do something like that :
setBoard((currentBoard)=> {
currentBoard.lanes[0].cards = [...currentBoard.lanes[0].cards, whateverCardYouWantToAdd ]
return {... currentBoard} //necessary to create a new object because else the hook won't be updated
})
Maybe with this.
const addValue = () => {
let t = [...board];
t[0].lanes.cards.push({
id: null,
title: "Bug #1",
description: "Can AI make memes"
});
};

how to loop through two array in react?

I'm trying to get my data from 2 arrays:
Here is my function:
function Cart({ basketProps }) {
let productsInCart = [];
Object.keys(basketProps.products).forEach(function (item) {
if (basketProps.inCart) {
productsInCart.push(basketProps.products);
}
console.log(productsInCart);
});
...
...
...
}
when i do console.log it return me this:
[{…}]
0:
products: Array(1)
0:
img_url1: "https://thebeuter.com/wp-content/uploads/2020/06/38-1.jpg"
price: 1290000
title: "BEUTER BACK2BACK ZIPPER WHITE JACKET"
__proto__: Object
length: 1
__proto__: Array(0)
__proto__: Object
length: 1
__proto__: Array(0)
How can I use .map to loop thru these?
Updated:
When I do console.log(basketProps). It gave me this:
basketNumbers: 1
cartCost: 1290000
inCart: true
numbers: 1
products:
products: Array(1)
0: {title: "BEUTER BACK2BACK ZIPPER WHITE JACKET", price: 1290...}
You dont require the 'Object.keys' function, since inCart is already available at outer level.
if (basketProps.inCart) {
productsInCart.push(basketProps.products);
}
let total =0;
productsInCart.map(cartProduct=>{
total = total + cartProduct.price;
}
You can run the map function on productInCart array like above.
To select all products inCart use:
filter to select all products inCart
map (or flatMap) to select products property you're interested in
I use flatMap because it makes list easier to render - it makes an array of products, not an array of product arrays. flatMap first maps each element using a mapping function, then flattens the result into a new array (it is identical to a map() followed by a flat() of depth 1).
function App() {
return <Cart basketProps={DATA} />;
}
function Cart({ basketProps }) {
const productsInCart = basketProps
.filter(product => product.inCart)
.flatMap(product => product.products);
return (
<div className="App">
<ul>
{productsInCart.map(product => (
<li>{product.title}</li>
))}
</ul>
</div>
);
}
const DATA = [
{ inCart: true, products: [
{ title: "PRODUCT 1", price: 10 },
{ title: "PRODUCT 2", price: 20 }
]},
{ inCart: false, products: [
{ title: "PRODUCT 3", price: 30 },
{ title: "PRODUCT 4", price: 40 }
]},
{ inCart: true, products: [
{ title: "PRODUCT 5", price: 50 },
{ title: "PRODUCT 6", price: 60 }
]}
];
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
I assume basketProps.products is an object array which has list of products, and has inCart = true if the product is in cart
In that case your code to get the products in cart should be like this
let productsInCart = [];
if(basketProps && Array.isArray(basketProps.products)) {
productsInCart = basketProps.products.filter(function (item) {
return item.inCart ;
});
}
console.log(productsInCart )
or if you are using arrow functions, the one liner would be (add array validation as in example above)
let productsInCart = basketProps.products.filter((item)=>(item.inCart));
console.log(productsInCart);

ant design table with dynamic children columns

I'm using ant's table and trying to setup dynamic columns.
What I need is a table that shows a list of users with their performance for each of the classes as in the example below.
Details: I have a grouped column Performance that can have different sub-columns (current example shows columns science and physics). I'm calling renderContent() which sets up an object that has property children. I found this "solution" from ant's example here. The problem is that ant's example outputs children prop type string, while my function outputs prop type array. Which results in the error.
Here is a link to sandbox:
https://codesandbox.io/embed/ecstatic-cookies-4nvci?fontsize=14
Note: if you uncomment children array in columns [line 46-59], you will see what my expected result should be.
The render method shouldn't return the object with children array. To use the render method, you would have to return a valid React component (or simply HTML tag ---like span).
However in your case, I prefer we extract subjects before passing it into the table and then generate children array dynamically. Something like below:
const renderContent = (value, row, index) => {
return setupPerformance(value)
};
const setupPerformance = performance => {
return performance.map(p => {
const { id, title, percentage } = p;
return <span>{percentage}%</span>
});
};
const data = [
{
key: 0,
firstName: "John",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 75
},
{
id: 2,
title: "physics",
percentage: 36
}
]
},
{
key: 1,
firstName: "Ann",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 68,
timeSpent: 50,
completionDate: "2019-02-07"
},
{
id: 2,
title: "physics",
percentage: 100
}
]
}
];
let subjects = data[0].performance
const columns = [
{
title: "Full Name",
children: [
{
title: "firstName",
dataIndex: "firstName",
key: "firstName"
},
{
title: "lastName",
dataIndex: "lastName",
key: "lastName"
}
]
},
{
title: "Performance",
dataIndex: "performance",
children:
subjects.map(e => {
return {
title: e.title,
dataIndex: "performance["+(e.id-1)+"].percentage",
key: "key-"+e.id,
render: value => <span>{value}%</span>
}
})
}
];
Because of the solution in answer from Mobeen does not work anymore, I have tried to solve this.
I have extended the render method for the children columns of performance column:
...
{
title: "Performance",
dataIndex: "performance",
children: subjects.map((assessment) => {
const { title, id } = assessment;
return {
title,
dataIndex: "performance",
key: id,
render: (values) =>
values.map((value, index) => {
let ret;
if (index === id - 1) ret = values[index].percentage + "%";
return ret;
})
};
})
}
...
It returns only the percentage value of the subject with the corresponding id.
It is not very clean, but it works.
Check the solution in sandbox: https://codesandbox.io/s/prod-lake-7n6zgj

In a one to many relationship, is there a way to filter the parent objects via an attribute of the child?

I have a rails backend with the following relationships: a USER has many MOVES. a Move has many boxes. A Box has many items.
I have page that lists all of the boxes inside of a specific move and this page ALSO lists all of the items for that specific move. I have a search bar on this page that enables you to search for specific items. I am able to filter my items display, however, i cannot figure out how to filter my boxes BY the searching for the name of the items WITHIN them.
I have tried iterating over the array of Box objects, and then iterating over the key within each box that points to its array of items. I am able to get the filtered ITEMS, but I dont know how to translate that back to reflect the BOXES with those items.
For instance, in the console I tried:
var filteredBoxes = boxes.map((box) => {
return box.items.filter((i) => {
return i.name.includes(this.state.searchTerm)
})
})
But it keeps returning items, not the boxes im trying to filter.
This is how the JSON looks when I fetch my boxes. I used a serializer to list the items as well:
{
id: 1,
name: "Bedding",
category: "Bedroom",
move_id: 1,
move: {
id: 1,
name: "Leaving for College",
date: "2019-08-12",
user_id: 1
},
items: [
{
id: 1,
name: "Comforter",
image: "https://www.shopmarriott.com/images/products/v2/lrg/Marriott-down-duvet-comforter-MAR-112_1_lrg.jpg",
box_id: 1
},
{
id: 2,
name: "Throw Pillows",
image: "https://media.kohlsimg.com/is/image/kohls/3427815?wid=500&hei=500&op_sharpen=1",
box_id: 1
}
]
},
{
id: 2,
name: "Random Blankets",
category: "Den",
move_id: 1,
move: {
id: 1,
name: "Leaving for College",
date: "2019-08-12",
user_id: 1
},
items: [
{
id: 3,
name: "Pillows",
image: "https://www.greatsleep.com/on/demandware.static/-/Sites-tbp-master-catalog/default/dw9ff5c1cf/product-images/pillows/nautica/down-alt-pillow-2-pack-na-91644/nautica-down-alternative-pillow-2-pack_91644-icon-2500x2500.jpg",
box_id: 2
},
{
id: 4,
name: "Stuffed Animals",
image: "https://s7d9.scene7.com/is/image/JCPenney/DP0817201617082870M?resmode=sharp2&op_sharpen=1&wid=550&hei=550",
box_id: 2
}
]
},
{
id: 3,
name: "Cleaning Supplies",
category: "Kitchen",
move_id: 1,
move: {
id: 1,
name: "Leaving for College",
date: "2019-08-12",
user_id: 1
},
items: [
{
id: 5,
name: "Pillows",
image: "https://www.greatsleep.com/on/demandware.static/-/Sites-tbp-master-catalog/default/dw9ff5c1cf/product-images/pillows/nautica/down-alt-pillow-2-pack-na-91644/nautica-down-alternative-pillow-2-pack_91644-icon-2500x2500.jpg",
box_id: 3
},
{
id: 6,
name: "Stuffed Animals",
image: "https://s7d9.scene7.com/is/image/JCPenney/DP0817201617082870M?resmode=sharp2&op_sharpen=1&wid=550&hei=550",
box_id: 3
}
]
}
you just have to iterate boxes, and so filter items. Based on these filtered items you may choose to return or not a box to the list.
const data = [{
id:1,
name:"Bedding",
category:"Bedroom",
move_id:1,
move:{
id:1,
name:"Leaving for College",
date:"2019-08-12",
user_id:1
},
items:[
{
id:1,
name:"Comforter",
image:"https://www.shopmarriott.com/images/products/v2/lrg/Marriott-down-duvet-comforter-MAR-112_1_lrg.jpg",
box_id:1
},
{
id:2,
name:"Throw Pillows",
image:"https://media.kohlsimg.com/is/image/kohls/3427815?wid=500&hei=500&op_sharpen=1",
box_id:1
}
]
},
{
id:2,
name:"Random Blankets",
category:"Den",
move_id:1,
move:{
id:1,
name:"Leaving for College",
date:"2019-08-12",
user_id:1
},
items:[
{
id:3,
name:"Pillows",
image:"https://www.greatsleep.com/on/demandware.static/-/Sites-tbp-master-catalog/default/dw9ff5c1cf/product-images/pillows/nautica/down-alt-pillow-2-pack-na-91644/nautica-down-alternative-pillow-2-pack_91644-icon-2500x2500.jpg",
box_id:2
},
{
id:4,
name:"Stuffed Animals",
image:"https://s7d9.scene7.com/is/image/JCPenney/DP0817201617082870M?resmode=sharp2&op_sharpen=1&wid=550&hei=550",
box_id:2
}
]
},
{
id:3,
name:"Cleaning Supplies",
category:"Kitchen",
move_id:1,
move:{
id:1,
name:"Leaving for College",
date:"2019-08-12",
user_id:1
},
items:[
{
id:5,
name:"Pillows",
image:"https://www.greatsleep.com/on/demandware.static/-/Sites-tbp-master-catalog/default/dw9ff5c1cf/product-images/pillows/nautica/down-alt-pillow-2-pack-na-91644/nautica-down-alternative-pillow-2-pack_91644-icon-2500x2500.jpg",
box_id:3
},
{
id:6,
name:"Stuffed Animals",
image:"https://s7d9.scene7.com/is/image/JCPenney/DP0817201617082870M?resmode=sharp2&op_sharpen=1&wid=550&hei=550",
box_id:3
}
]
}];
const searchTerm = "Animals"
// function to filter sub-items
const filterItems = items => items.filter((i) => searchTerm ? i.name.includes(searchTerm) : i.name);
const filteredBoxes = data.map(boxes => {
//filter sub-items
const items = filterItems(boxes.items);
//in case there is any item, return that boxes
if (items.length) {
return Object.assign({}, boxes, { items })
}
// in case there is nothing, return false
return false;
}).filter(Boolean); // filter the boxes list removing the false values
console.log('filteredBoxes', filteredBoxes);

How to render a React component with survey.js?

Here is a working survey.js with react example: http://plnkr.co/edit/qXdeQa6x2FHRg0YrOlPL?p=preview
My html also does have in head the <script src="https://unpkg.com/survey-react"></script> included.
In my main.jsx I'm calling the rendering of a div that will be filled up based on a json file and by multiple react components, in my json array I have a questions json object too for survey.js. What I would like to achieve is to render a survey when the questions object is being reached in parsing the array.
In my top level component I have this calling for it:
if (section.hasOwnProperty('questions')){
return <Questions key={i+"questions"} id={i+"questions"} questions={section.questions} />;
The Questions.jsx:
var React = require('react');
var ReactDOM = require('react-dom');
var Survey = require('survey-react');
var Questions = React.createClass({
render: function() {
Survey.Survey.cssType = "bootstrap";
Survey.defaultBootstrapCss.navigationButton = "btn btn-green";
window[this.props.id] = new Survey.Model( this.props.questions );
var questionid = this.props.id;
var idi = this.props.id.replace("questions","");
return (
<dt>
<div id={this.props.id}></div>
</div>
</dt>
);
}
});
module.exports = Questions;
ReactDOM.render(<Survey.Survey model={window[questionid]} />, document.getElementById({questionid}));
It does compile without errors but then in the browser I get the console errors:
TypeError: WEBPACK_IMPORTED_MODULE_1_react is undefined[Learn
More]
ReferenceError: questionid is not defined
Seems like I try to do it the wrong way, but how to do it right? I'm new to react and not familiar with using reactDOM inside of a component and is my first project on survey.js as well.
Thank you for helping out.
Please check this code:
import React, { Component } from 'react';
import { render } from 'react-dom';
import * as Survey from 'survey-react';
import 'survey-react/survey.css';
import 'bootstrap/dist/css/bootstrap.css'
import './style.css';
class App extends Component {
componentWillMount() {
Survey.Survey.cssType = "bootstrap";
Survey.defaultBootstrapCss.navigationButton = "btn btn-green";
}
render() {
var json = { title: "Product Feedback Survey Example", showProgressBar: "top", pages: [
{questions: [
{ type: "matrix", name: "Quality", title: "Please indicate if you agree or disagree with the following statements",
columns: [{ value: 1, text: "Strongly Disagree" },
{ value: 2, text: "Disagree" },
{ value: 3, text: "Neutral" },
{ value: 4, text: "Agree" },
{ value: 5, text: "Strongly Agree" }],
rows: [{ value: "affordable", text: "Product is affordable" },
{ value: "does what it claims", text: "Product does what it claims" },
{ value: "better then others", text: "Product is better than other products on the market" },
{ value: "easy to use", text: "Product is easy to use" }]},
{ type: "rating", name: "satisfaction", title: "How satisfied are you with the Product?",
mininumRateDescription: "Not Satisfied", maximumRateDescription: "Completely satisfied" },
{ type: "rating", name: "recommend friends", visibleIf: "{satisfaction} > 3",
title: "How likely are you to recommend the Product to a friend or co-worker?",
mininumRateDescription: "Will not recommend", maximumRateDescription: "I will recommend" },
{ type: "comment", name: "suggestions", title:"What would make you more satisfied with the Product?", }
]},
{questions: [
{ type: "radiogroup", name: "price to competitors",
title: "Compared to our competitors, do you feel the Product is",
choices: ["Less expensive", "Priced about the same", "More expensive", "Not sure"]},
{ type: "radiogroup", name: "price", title: "Do you feel our current price is merited by our product?",
choices: ["correct|Yes, the price is about right",
"low|No, the price is too low for your product",
"high|No, the price is too high for your product"]},
{ type: "multipletext", name: "pricelimit", title: "What is the... ",
items: [{ name: "mostamount", title: "Most amount you would every pay for a product like ours" },
{ name: "leastamount", title: "The least amount you would feel comfortable paying" }]}
]},
{ questions: [
{ type: "text", name: "email",
title: "Thank you for taking our survey. Your survey is almost complete, please enter your email address in the box below if you wish to participate in our drawing, then press the 'Submit' button."}
]}
]};
var model = new Survey.Model(json);
return (
<Survey.Survey model={model}/>
);
}
}
render(<App />, document.getElementById('root'));
and here a live example: https://stackblitz.com/edit/surveyjs-react-stackoverflow45544026

Resources