Updating state array of objects from api call - javascript

Hy, let me explain my problem, i have a state of tag called tag_data :
const [tagData, setTagData] = useState([
{ key: '1', label: 'Music', active: 0 },
{ key: '2', label: 'Sport', active: 0 },
{ key: '3', label: 'Dance', active: 0 },
{ key: '4', label: 'Cook', active: 0},
{ key: '5', label: 'Video Games', active: 0},
{ key: '6', label: 'Travel', active: 0 },
{ key: '7', label: 'Picture', active: 0 },
{ key: '8', label: 'Animals', active: 0 },
{ key: '9', label: 'Coding', active: 0},
{ key: '10', label: 'Party', active: 0},
])
I do a api call for get ACTIVE tag from my user :
useEffect(() => {
const fetchData = async () => {
setLoad(true)
try {
const result = await axios.post('/user/activetag')
console.log(result.data.active_tag)
setTagData({
// update
})
} catch (error) {
console.log(error)
}
setLoad(false)
}
fetchData()
}, [])
Then the result store the active tags like this :
active_tag: Array(5)
0: {tag_id: 1, label: "Music"}
1: {tag_id: 2, label: "Sport"}
2: {tag_id: 3, label: "Dance"}
3: {tag_id: 4, label: "Cook"}
4: {tag_id: 5, label: "Video Games"}
I would like to update the tagData state and put active to 1 where the tag_id is equal to the key of tagData state, any idea ?
Full code :
import React, {useState, useEffect} from "react";
import { makeStyles } from '#material-ui/core/styles';
import Chip from '#material-ui/core/Chip';
import Paper from '#material-ui/core/Paper';
import DoneIcon from '#material-ui/icons/Done';
import axios from 'axios'
import Loading from '../../../../Loading/Loading'
const useStyles = makeStyles((theme) => ({
// style
})
export default function TagUser(){
const classes = useStyles();
const [load, setLoad] = useState(false)
const [tagData, setTagData] = useState([
{ key: '1', label: 'Music', active: 0 },
{ key: '2', label: 'Sport', active: 0 },
{ key: '3', label: 'Dance', active: 0 },
{ key: '4', label: 'Cook', active: 0},
{ key: '5', label: 'Video Games', active: 0},
{ key: '6', label: 'Travel', active: 0 },
{ key: '7', label: 'Picture', active: 0 },
{ key: '8', label: 'Animals', active: 0 },
{ key: '9', label: 'Coding', active: 0},
{ key: '10', label: 'Party', active: 0},
])
useEffect(() => {
const fetchData = async () => {
setLoad(true)
try {
const result = await axios.post('/user/activetag')
console.log(result.data)
setTagData({
// update
})
} catch (error) {
console.log(error)
}
setLoad(false)
}
fetchData()
}, [])
const handleDelete = (key) => {
//delete
}
const handleSubmit = (key) => {
//submit
}
if(load){
return <Loading/>
} else {
return(
<Paper variant="outlined" square component="span" className={classes.root}>
{
tagData.map((data) => {
if (data.active === 0) {
return (
<li key={data.key}>
<Chip
variant="outlined"
color="secondary"
label={data.label}
className={classes.chip}
onDelete={() => handleSubmit(data.key)}
deleteIcon={<DoneIcon />}
/>
</li>
)
} else {
return (
<li key={data.key}>
<Chip
color="secondary"
label={data.label}
className={classes.chip}
onDelete={() => handleDelete(data.key)}
/>
</li>
)
}
})
}
</Paper>
)
}
}

Disclosure: I am the author of the suspense-service library used in this answer.
If you're open to using a 3rd-party library, it can significantly simplify your data-fetching logic. You won't need a load state, or a useEffect(), the component will only render when the list is ready:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Chip from '#material-ui/core/Chip';
import Paper from '#material-ui/core/Paper';
import DoneIcon from '#material-ui/icons/Done';
import axios from 'axios';
import { createService, useService } from 'suspense-service';
import Loading from '../../../../Loading/Loading';
const defaultTags = [
{ key: '1', label: 'Music', active: 0 },
{ key: '2', label: 'Sport', active: 0 },
{ key: '3', label: 'Dance', active: 0 },
{ key: '4', label: 'Cook', active: 0},
{ key: '5', label: 'Video Games', active: 0},
{ key: '6', label: 'Travel', active: 0 },
{ key: '7', label: 'Picture', active: 0 },
{ key: '8', label: 'Animals', active: 0 },
{ key: '9', label: 'Coding', active: 0},
{ key: '10', label: 'Party', active: 0},
];
const UserActiveTags = createService(async (allTags) => {
try {
const result = await axios.post('/user/activetag');
console.log(result.data.active_tag);
const activeTags = result.data.active_tag.map((tag) => tag.tag_id);
const activeTagsSet = new Set(activeTags);
return allTags.map((tag) => ({
...tag,
active: activeTagsSet.has(tag.key) ? 1 : 0
}));
} catch (error) {
console.log(error);
return allTags;
}
});
export default function TagUser() {
return (
<UserActiveTags.Provider request={defaultTags} fallback={<Loading />}>
<TagList />
</UserActiveTags.Provider>
);
}
const useStyles = makeStyles((theme) => ({
// style
}));
function TagList() {
const { root, chip } = useStyles();
const tagData = useService(UserActiveTags);
const handleDelete = (key) => {
//delete
};
const handleSubmit = (key) => {
//submit
};
const tagList = tagData.map(({ active, key, label }) => {
const props = active === 0
? { variant: 'outlined', onDelete: () => handleSubmit(key), deleteIcon: <DoneIcon /> }
: { variant: 'default', onDelete: () => handleDelete(key) };
return (
<li key={key}>
<Chip
color="secondary"
label={label}
className={chip}
{...props}
/>
</li>
);
});
return (
<Paper variant="outlined" square={true} component="span" className={root}>
{tagList}
</Paper>
);
}
If you need tagData to be stateful, then
const tagData = useService(UserActiveTags);
needs to be updated to this:
const initialTagData = useService(UserActiveTags);
const [tagData, setTagData] = useState(initialTagData);
useEffect(() => {
setTagData(initialTagData);
}, [initialTagData]);

I think you could something like this,
In your useEffect(),
const result = await axios.post('/user/activetag');
const filteredTags = tagData.map((e) => {
const checkActive = result.data.some(i => i.tag_id == e.key);
if(checkActive){
const temp = {...e};
temp.active = 1;
return temp;
}
return e;
});
setTagData(filteredTags);
Hope that works!

Related

How to pass more than one children - React JS

Hi I am developing a Table components where I need to populate the data in table body dynamically. I am maintaining the bodyData in arrayOf:[{}]. Cells will be created dynamically based on the values in the mock data. I need to pass four different data in body from mockdata. Now all are getting passed as children. Now I need to display one of the data in table cell in badge format. For this I am trying to pass multiple children so that I can render one of the children in badge format. But When I am passing more than one children component is rendering last children value only. Could any one help me how to pass (Brand value in the table in form of badge). Thanks in advance. I have wrote down the working code and mockdata.
import mockData from './mock-data/';
const createCell = cell => ({ key: cell.key, children: cell.children});
//tried to pass like cell => ({ key: cell.key, children: cell.customerName,
//children:cell.customerAddr})
const createCellsForRow = cells => cells.map(cell => createCell(cell));
const StTable = () => {
const [selectedKey, setSelectedKey] = useState([]);
const handleRowToggle = (event, metaData) => {
event.preventDefault();
if (selectedKey !== metaData.key) {
setSelectedKey(metaData.key);
}
};
const createRow = rowData => (
{
key: rowData.key,
cells: createCellsForRow(rowData.cells),
toggleAction: {
metaData: { key: rowData.key },
onToggle: handleRowToggle,
isToggled: selectedKey === rowData.key,
toggleLabel: rowData.toggleText,
},
}
);
const createRows = data => data.map(childItem => createRow(childItem));
return (
<Table
summaryId="example-single-select"
summary="This table shows an implementation of single row selection."
numberOfColumns={4}
cellPaddingStyle="standard"
rowStyle="toggle"
dividerStyle="horizontal"
headerData={{
selectAllColumn: {
checkLabel: 'Single Selection',
},
cells: [
{ key: 'cell-0', id: 'toggle-0', children: 'Name' },
{ key: 'cell-1', id: 'toggle-1', children: 'Address' },
{ key: 'cell-2', id: 'toggle-2', children: 'Phone Number' },
{ key: 'cell-3', id: 'toggle-3', children: 'Brand' },//I need to pass this is as
//badge format.
],
}}
bodyData={[
{
rows: createRows(mockData),
},
]}
/>
);
};
export default StTable;
//mockData Sample
[
{
"key":"row-0",
toggleText:"txt",
"cells":[
{ key: 'cell-0', id: 'toggle-0', children: 'ABC' },
{ key: 'cell-1', id: 'toggle-1', children: 'ABC123' },
{ key: 'cell-2', id: 'toggle-2', children: 'P1234567890' },
{ key: 'cell-3', id: 'toggle-3', children: ['A', 'B'] },
]
]
//I am trying to change the structure because (I need to display the brand in badge format)
[
{
"key":"row-0",
toggleText:"txt",
"cells":[
{ key: 'cell-0', id: 'toggle-0', customerName: 'ABC' },
{ key: 'cell-1', id: 'toggle-1', customerAddr: 'ABC123' },
{ key: 'cell-2', id: 'toggle-2', customerPhNo: 'P1234567890' },
{ key: 'cell-3', id: 'toggle-3', customerBrand: ['A', 'B'] },
]
]
//Table Component
const createCell = cell => ({ key: cell.key, children: cell.title });
const createCellsForRow = cells => cells.map(cell => createCell(cell));
const Table = () => {
const [selectedKey, setSelectedKey] = useState([]);
const handleRowToggle = (event, metaData) => {
event.preventDefault();
if (selectedKey !== metaData.key) {
setSelectedKey(metaData.key);
}
};
const createRow = rowData => (
{
key: rowData.key,
cells: createCellsForRow(rowData.cells),
toggleAction: {
metaData: { key: rowData.key },
onToggle: handleRowToggle,
isToggled: selectedKey === rowData.key,
toggleLabel: rowData.toggleText,
},
}
);
const createRows = data => data.map(childItem => createRow(childItem));
return (
<Table
numberOfColumns={4}
cellPaddingStyle="standard"
rowStyle="toggle"
dividerStyle="horizontal"
headerData={{
selectAllColumn: {
checkLabel: 'Single Selection',
},
cells: [
{ key: 'cell-0', id: 'toggle-0', children: 'Name' },
{ key: 'cell-1', id: 'toggle-1', children: 'Address' },
{ key: 'cell-2', id: 'toggle-2', children: 'Phone Number' },
{ key: 'cell-3', id: 'toggle-3', children: 'Email Id' },
],
}}
bodyData={[
{
rows: createRows(mockData),
},
]}
/>
);
};
export default Table;

how to solve asynchronous behaviour in search Box react

so im trying to implement a search box with useState and useEffect. we have an array of objects and want to filter it according to our search term. here is my implementation:
import React, {useEffect, useState} from "react";
const array = [
{ key: '1', type: 'planet', value: 'Tatooine' },
{ key: '2', type: 'planet', value: 'Alderaan' },
{ key: '3', type: 'starship', value: 'Death Star' },
{ key: '4', type: 'starship', value: 'CR90 corvette' },
{ key: '5', type: 'starship', value: 'Star Destroyer' },
{ key: '6', type: 'person', value: 'Luke Skywalker' },
{ key: '7', type: 'person', value: 'Darth Vader' },
{ key: '8', type: 'person', value: 'Leia Organa' },
];
let available = []
const Setup = () => {
const [state, setState] = useState('');
useEffect(() => {
available = array.filter(a => a.value.startsWith(state));
},[state])
const show = state ? available : array;
return <>
<input value={state} onChange={e => setState(e.target.value)} type="text" className="form"/>
{show.map(a => {
return <Data id={a.key} key={parseInt(a.key)} value={a.value} type={a.type}/>
})}
</>
}
const Data = (props) => {
return <>
<div>
<p>{props.value}</p>
</div>
</>
}
export default Setup;
the problem starts when we give our search box a valid search term(like 'T'). i expect it to change the output accordingly(to only show 'Tatooine') but the output does not change.
meantime if you add another character to search term(like 'a' which would set our search term to 'Ta') it will output the expected result. in the other words, search term is not applied synchronously. do you have any idea why is that
The useEffect hook is triggered when the component mounts, rerenders or unmounts. In your case, the change of the search field causes a rerender because of the change of the state. This results in your useEffect triggering after the state change and is too late for what you need.
If you type "Ta" into your field, you'll see it works, but it appears as if the search is one step behind.
You can simply remove the use of useEffect and filter when you render. This means you can also remove the whole logic around the available and show variables:
const Setup = () => {
const [state, setState] = useState("");
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{array
.filter((a) => a.value.startsWith(state))
.map((a) => (
<Data
id={a.key}
key={parseInt(a.key, 10)}
value={a.value}
type={a.type}
/>
))}
</>
);
};
There is some good information in the Using the Effect Hook docs.
You just add toLowerCase mehtod to your filter function. just like this :
import React, { useEffect, useState } from "react";
const array = [
{ key: "1", type: "planet", value: "Tatooine" },
{ key: "2", type: "planet", value: "Alderaan" },
{ key: "3", type: "starship", value: "Death Star" },
{ key: "4", type: "starship", value: "CR90 corvette" },
{ key: "5", type: "starship", value: "Star Destroyer" },
{ key: "6", type: "person", value: "Luke Skywalker" },
{ key: "7", type: "person", value: "Darth Vader" },
{ key: "8", type: "person", value: "Leia Organa" }
];
let available = [];
const Setup = () => {
const [state, setState] = useState("");
useEffect(() => {
available = array.filter((a) => a.value.toLowerCase().startsWith(state));
}, [state]);
const show = state ? available : array;
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{show.map((a) => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = (props) => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;
and here is the working example : here
You can simply just pull out useEffect.
import React, { useState } from 'react';
const array = [
{ key: '1', type: 'planet', value: 'Tatooine' },
{ key: '2', type: 'planet', value: 'Alderaan' },
{ key: '3', type: 'starship', value: 'Death Star' },
{ key: '4', type: 'starship', value: 'CR90 corvette' },
{ key: '5', type: 'starship', value: 'Star Destroyer' },
{ key: '6', type: 'person', value: 'Luke Skywalker' },
{ key: '7', type: 'person', value: 'Darth Vader' },
{ key: '8', type: 'person', value: 'Leia Organa' },
];
let available = [];
const Setup = () => {
const [state, setState] = useState('');
available = array.filter(a => a.value.startsWith(state));
const show = state ? available : array;
return (
<>
<input
value={state}
onChange={e => setState(e.target.value)}
type='text'
className='form'
/>
{show.map(a => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = props => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;
This must solve it
import React, { useEffect, useState } from "react";
const array = [
{ key: "1", type: "planet", value: "Tatooine" },
{ key: "2", type: "planet", value: "Alderaan" },
{ key: "3", type: "starship", value: "Death Star" },
{ key: "4", type: "starship", value: "CR90 corvette" },
{ key: "5", type: "starship", value: "Star Destroyer" },
{ key: "6", type: "person", value: "Luke Skywalker" },
{ key: "7", type: "person", value: "Darth Vader" },
{ key: "8", type: "person", value: "Leia Organa" }
];
const Setup = () => {
const [state, setState] = useState("");
const [available, setAvailable] = useState(array);
useEffect(() => {
setAvailable(array.filter((a) => a.value.startsWith(state)));
}, [state]);
return (
<>
<input
value={state}
onChange={(e) => setState(e.target.value)}
type="text"
className="form"
/>
{available.map((a) => {
return (
<Data
id={a.key}
key={parseInt(a.key)}
value={a.value}
type={a.type}
/>
);
})}
</>
);
};
const Data = (props) => {
return (
<>
<div>
<p>{props.value}</p>
</div>
</>
);
};
export default Setup;

How to filter the data array student based on the room using react hook form

index.tsx
data = {
room: [
{
id: 1,
name: 'room1'
},
{
id: 2,
name: 'room2'
},
{
id: 3,
name: 'room3'
}
],
student: [
{
id: 1,
room: 'room1',
name: 'josh'
},
{
id: 2,
room: 'room1',
name: 'jake'
}
]
}
const _ROOM = data['room'];
const _STUDENT = data['student'];
const form = {
config: [
{
label: "Room",
name: "room",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Room"
data={
_ROOM && _ROOM.length > 0 ?
_ROOM.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => {
control.setValue('room', val);
}}
value={control.getValues()['room']}
/>
);
}
},
{
label: "Student",
name: "student",
type: "select",
rule: yup.string().required(),
renderer: (data: any) => {
const { control, register, errors } = useFormContext();
return (
<SelectPicker
placeholder="Student"
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.map(x => ({ label: x.name, value: x.id })) : []
}
style={{ width: '100%' }}
onChange={(val) => control.setValue('student', val)}
value={control.getValues()['student']}
/>
);
}
}]}
How to filter the student based on the room. for example I select the room1 then on the student it will filter which it has value room1. I try to filter inside the onchange in room but it doesn't work also not filtering or display the console log. also I used the state then set inside the onChange but it doesn't work also.
Take a look at this: https://react-hook-form.com/advanced-usage/#ConditionalControlledComponent
Basically, you can watch for changes in room and filter out the options in the student field.
const {room} = watch();
And in the SelectPicker, modify the data prop to:
data={
_STUDENT && _STUDENT.length > 0 ?
_STUDENT.filter(s => s.room === room).map(x => ({ label: x.name, value: x.id })) : []
}

Displaying mapped data without being clicked before showing

I am having a little difficulty with my data. I was able to get all mapped data currently but without me clicking the categories, the data won't be displayed.
Here's an example:
const categoriesData = [
{
name: 'All',
label: 'All',
total: '18',
className: 'activeCategory'
},
{
name: 'Business Services',
label: 'Business_Services',
total: '18'
},
{
name: 'Design Services',
label: 'Design_Services',
total: '18'
},
{
name: 'Education Services',
label: 'Education_Services',
total: '18'
},
{
name: 'Finance Services',
label: 'Finance_Services',
total: '18'
},
{
name: 'IT Services',
label: 'IT_Services',
total: '18'
},
{
name: 'Legal Services',
label: 'Legal_Services',
total: '18'
},
{
name: 'Manufacturing',
label: 'Manufacturing',
total: '18'
},
{
name: 'Marketing',
label: 'Marketing',
total: '18'
}
];
const postsData = {
All: [
{ id: 1, name: 'Post 3' },
{ id: 2, name: 'Post 2' }
],
Business_Services: [
{ id: 1, name: 'Post 3' },
{ id: 2, name: 'Post 2' }
],
Design_Services: [
{ id: 1, name: 'Post 5' },
{ id: 2, name: 'Post 2' }
],
Education_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Finance_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
IT_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Legal_Services: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 2' }
],
Manufacturing: [
],
Marketing: [
{ id: 1, name: 'Post 1' },
{ id: 2, name: 'Post 27675' }
]
};
function fakePostsApi(catName) {
return new Promise((resolve) =>
setTimeout(() => {
resolve(postsData[catName]);
}, 1000)
);
}
function Main() {
const [categories, setCategories] = React.useState(categoriesData);
const [catName, setCatName] = React.useState();
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
if (catName) {
fakePostsApi(catName)
.then(setPosts);
}
}, [catName]);
return (
<div>
{categories.length > 0 ? (
categories.map((category, i) => {
return (
<button key={i} onClick={() => setCatName(category.label)}>
{category.name}
</button>
);
})
) : (
<p>Loading...</p>
)}
<div>
{posts.length === 0 ? (
<p>No posts...</p>
) : (
posts.map((post) => <div key={post.id}>{post.name}</div>)
)}
</div>
</div>
);
}
ReactDOM.render(
<Main />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
My main problem is: I want the values of all to be displayed first(just like active status) when page loads without being clicking the All button.
Set the default value of catName to be the label of the 0 item of the categories:
const [catName, setCatName] = React.useState(categories[0] && categories[0].label);
Or optional chaining (?.) if supported:
const [catName, setCatName] = React.useState(categories[0]?.label);
const categoriesData = [{"name":"All","label":"All","total":"18","className":"activeCategory"},{"name":"Business Services","label":"Business_Services","total":"18"},{"name":"Design Services","label":"Design_Services","total":"18"},{"name":"Education Services","label":"Education_Services","total":"18"},{"name":"Finance Services","label":"Finance_Services","total":"18"},{"name":"IT Services","label":"IT_Services","total":"18"},{"name":"Legal Services","label":"Legal_Services","total":"18"},{"name":"Manufacturing","label":"Manufacturing","total":"18"},{"name":"Marketing","label":"Marketing","total":"18"}];
const postsData = {"All":[{"id":1,"name":"Post 3"},{"id":2,"name":"Post 2"}],"Business_Services":[{"id":1,"name":"Post 3"},{"id":2,"name":"Post 2"}],"Design_Services":[{"id":1,"name":"Post 5"},{"id":2,"name":"Post 2"}],"Education_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Finance_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"IT_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Legal_Services":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 2"}],"Manufacturing":[],"Marketing":[{"id":1,"name":"Post 1"},{"id":2,"name":"Post 27675"}]};
function Main() {
const [categories, setCategories] = React.useState(categoriesData);
const [catName, setCatName] = React.useState(categories[0] && categories[0].label);
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
if (catName) {
fakePostsApi(catName)
.then(setPosts);
}
}, [catName]);
return (
<div>
{categories.length > 0 ? (
categories.map((category, i) => {
return (
<button key={i} onClick={() => setCatName(category.label)}>
{category.name}
</button>
);
})
) : (
<p>Loading...</p>
)}
<div>
{posts.length === 0 ? (
<p>No posts...</p>
) : (
posts.map((post) => <div key={post.id}>{post.name}</div>)
)}
</div>
</div>
);
}
function fakePostsApi(catName) {
return new Promise((resolve) =>
setTimeout(() => {
resolve(postsData[catName]);
}, 1000)
);
}
ReactDOM.render(
<Main />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>

filtering array to remove item react

I'm trying to use filter to remove an object from the array. When I click on the recently added item it will console.log the items id but doesn't remove the item from the array, not sure where I'm going wrong?
import React, { Component } from 'react'
import { reduxForm } from 'redux-form'
// import Input from '../forms/Input/Input'
import actions from './actions'
import { connect } from 'react-redux'
import styles from './Catalogue.styl'
// import Checklist from './Checklist/Checklist'
#reduxForm({
form: 'orderReview',
})
#connect(null, actions)
export default class Catalogue extends Component {
constructor() {
super()
this.state = {
filterText: '',
favourites: [],
data: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
handleFilterUpdate = event => {
this.setState({
filterText: event.target.value,
})
}
addFavourite = (id) => {
const favList = this.state.favourites.concat([id])
this.setState({
favourites: favList,
})
console.log(id)
}
removeFavourite = (id) => {
console.log(id)
const removeFav = this.state.favourites.filter((_, i) => i !== id)
this.setState({
favourites: removeFav,
})
}
render() {
const {
data,
filterText,
favourites,
} = this.state
const NamesList = props => (
<div>
{props.data.filter(items => {
return items.label.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
})
.map(item => {
return (
<div
key={item.id}
onClick={() => props.addFavourite(item.id)}
>
{item.label}
</div>
)
}
)
}
</div>
)
const SaveName = props => {
const idList = props.favourites.map(id => {
const { label } = data[id]
return (
<div>
<br />
<li key={id} onClick={() => props.removeFavourite(data[id])}>{label}</li>
</div>
)
})
return (
<div>{idList}</div>
)
}
return (
<div>
<div className={styles.filtersList}>
<ul className={styles.filtersUl}>
<li className={styles.filtersLi}>znacky</li>
<li className={styles.filtersLi}>zeme</li>
<li className={styles.filtersLi}>Specialni</li>
</ul>
</div>
<input
type="text"
value={filterText}
onChange={this.handleFilterUpdate}
placeholder="Hledat podle nazvu"
/>
<NamesList data={data} addFavourite={this.addFavourite}/>
{filterText}
<SaveName favourites={favourites} removeFavourite={this.removeFavourite} />
</div>
)
}
}
You are iterating through your entire array, and you compare the parameter id with the index of the currently processed item of the array.
Instead, compare the item.id with the parameter, not with i:
class MyApp extends React.Component {
constructor() {
super()
this.state = {
favourites: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
removeFavourite = (id) => {
const removeFav = this.state.favourites.slice();
removeFav.splice(id, 1);
this.setState({
favourites: removeFav
})
}
render() {
return(
<ul>
{this.state.favourites.map((item, i) => <li key={item.id}>{item.label} <button onClick={this.removeFavourite.bind(this, i)}>Remove</button></li>)}
</ul>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="myApp"></div>
Alternatively, you could also just use splice() to remove the item directly:
class MyApp extends React.Component {
constructor() {
super()
this.state = {
favourites: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
removeFavourite = (id) => {
const removeFav = this.state.favourites.filter(item => item.id-1 != id)
this.setState({
favourites: removeFav
})
}
render() {
return(
<ul>
{this.state.favourites.map((item, i) => <li key={item.id}>{item.label} <button onClick={this.removeFavourite.bind(this, i)}>Remove</button></li>)}
</ul>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="myApp"></div>

Categories

Resources