React.js: Loop through a state array to create multiple JSON request - reactjs

I'm trying to create this type of JSON request for multiple deletion of records
[
{
"id" : "618D7054-339E-4B58-ADD2-45C0A4D2B915",
"maker": "TESTMAKER",
"status": "DELETED"
},
{
"id" : "E698C439-EDB8-4652-9365-F64B3000B97E",
"maker": "TESTMAKER",
"status": "DELETED"
}
]
The screen is composed of a datatable with checkboxes
I can now get the id of each record when checked with this code:
const selectRowBrstn = {
mode: 'checkbox',
clickToSelect: true,
clickToEdit: false,
onSelect: (row, isSelect, rowIndex, e) => {
console.log("selectRowBrstn - onSelect row >>> " + row.id)
setCheckedBrstnRecords([...checkedBrstnRecords, row.id]);
},
onSelectAll: (isSelect, rows, e) => {
console.log("selectRowBrstn - onSelectAll rowIndex >>> " + rows)
}
};
With this, I'm putting the values in the state (array):
const [checkedBrstnRecords, setCheckedBrstnRecords] = useState([]);
Now, I have an array of ids in the state
The challenge now is for me to create multiple axios POST requests with different ids coming from the state
I have little idea on how to manipulate objects in the state
May I know what's the best approach for this?
Btw, here is the axios POST request:
deleteBrstnMaintenance(checkedBrstnRecords){
return axios.post(`${API_URL_SAVE_BANK_MAINTENANCE}`,
[
{
}
])
}
TIA

You could just iterate over your state. It depends at which time you want to fire the requests and how many at a specific time.
What you are probably looking for is:
useEffect(() => { // useEffect will be called if the variable in the provided array (in this case checkedBrstnRecords changes
Object.keys(checkedBrstnRecords).forEach(el => { // iterate over the state
const timer = setTimeout(() => {
const currElem = checkedBrstnRecords[el]; // here is your element
let res = await axios.post(API_URL_SAVE_BANK_MAINTENANCE);
}, 70);
return () => clearTimeout(timer); // clear timer
})
}, [checkedBrstnRecords]);

Related

after sort, the ui not render with new data

im trying to list out the list after sort by status. This is my code, it can be sort in order.But the problem is, at the ui screen, it still previous value which it not render after do sorting.
const [listData, setListData] = useState(null)
let statusOrder={
'Active':1,
'In progress'
}
let list=[
{
sample: '1',
data:[
{
name:'name1',
status:'In Progress'
},
{
name:'name2',
status:'Active'
}
]
},
{
sample:'2',
data:[
{
name:'name1',
status:'In Progress'
},
{
name:'name2',
status:'Active'
}
]
}
]
const function = () => {
setListData(list)
for(let i=0; i<listData.length; i++){
listData[i].data.sort(a,b) => statusOrder[a.status] - statusOrder[b.status]
if((index+1) == listData.length)
setData(listData)
}
}
useEffect(() => {
sort()
},[])
I already try this approach, but still not render.
setListData([].concat(listData[i].data).sort((a, b) => statusOrder[a.status] - statusOrder[b.status]))
Like mentioned in the comments you're not really setting a new state since the "new" state is the same object as the old one.
Your function is called function which is a reserved keyword in the JS. So I assume you mean sort like you're using in your useEffect.
To handle the sort or your list you can try this
const sort = () => {
// give a callback to the setState which receives the prevState
setListData((prevListData) => {
// map over the prevState which returns a new array
const newListData = prevListData.map((item) => {
// return a new object with the item and sorted data
return {
...item,
data: item.data.sort(
(a, b) => statusOrder[a.status] - statusOrder[b.status]
),
};
});
return newListData;
});
};

React re-render problem with same data but different object of API

I am hitting a backend API which contains array of objects. I am storing the API data in a state called reportsTabData. This state contains array of objects and one object who's key is reports contains array of objects called options and each options contains array of objects called fields. I am setting the fields array of objects in my state called optionsTabData and I am creating a UI based on optionsTabData.
Example:
[
..some objects,
{
key: "report",
default: "incoming"
options: [
{
type: "incoming",
fields: [
{
colspan: "1"
key: "CLAppSchedulerRepDependentSelectors"
label: "Extension(s) Selection"
type: "CLAppSchedulerRepDependentSelectors"
}
]
},
{
type: "outgoing",
fields: [
{
colspan: "1"
key: "CLAppSchedulerRepDependentSelectors"
label: "Extension(s) Selection"
type: "CLAppSchedulerRepDependentSelectors"
}
]
}
]
}
]
The code works fine and UI is generated, but as you see, there are some reports who's fields contain same objects. The problem is the state update is not re-rendering for such use-case else it works fine.
Below is the code:
const [loader, setLoader] = useState(false);
const [optionsTabData, setOptionsTabData] = useState([]);
useEffect(() => {
callForApi()
},[])
const callForApi = async () => {
setLoader(true);
let response = await getRequest("api-url");
if (response.status == 200 && response.status <= 300) {
let adapter = response.data.schema;
setAdapterData(adapter);
setReportsTabData(adapter.report.fields);
setDistributionTabData(adapter.distribution.fields);
let data = [];
for (const ele of adapter.report.fields) {
if (ele.key === "report") {
for (const item of ele.options) {
if (item.type === ele.default) {
data = [...item.fields];
break;
}
}
break;
}
}
setOptionsTabData(data);
} else {
toastErrorMessagePopUp(response.data.detail);
}
setLoader(false);
};
const handleDropdownChange = (key, value) => {
if (key === "report") {
const schema = [...reportsTabData];
let report = schema.find((ele) => ele.key === "report");
let newOptions = []
for (const ele of report.options) {
if (ele.type === value) {
newOptions=[...ele.fields];
break;
}
}
setOptionsTabData([...newOptions])
}
}
How do I solve this? Apparently, doing something like await setOptionsTabData([]) before setOptionsTabData([...newOptions]) solves it but I think using await is a bad idea and also the value reflecting after a click in dropdown is sluggish and slow.
You can "force" the re-render by using a combination of JSON.stringify and JSON.parse
setOptionsTabData(JSON.parse(JSON.stringify(newOptions)))
It looks like it should be working. You are passing new array to setOptionsTabData which should trigger a rerender.
Using await is not a solution - functions returned by useState are synchronous and that should not make a difference. What's actually happening is you are resetting your optionsData state to an empty array and then setting it to the desired data.
Is there more code you can show? Maybe you have a useEffect somewhere that updates your UI with unfulfilled dependencies?

How can i auto refresh or render updated table data form database in material UI table after doing any operation in React?

Here useVideos() give us all videos form database. After adding a new video the new entry is not append in the Material UI table , but if I refresh the page then it's showing that new entry. Now I want to show this new entry after add operation. Please help me to do this! Thanks in Advance.
const initialState = [{}];
const reducer = (state, action) => {
switch (action.type) {
case "videos":
const data = [];
let cnt = 1;
action.value.forEach((video, index) => {
data.push({
sl: cnt,
noq: video.noq,
id: index,
youtubeID: video.youtubeID,
title: video.title,
});
cnt = cnt + 1;
});
return data;
default:
return state;
}
};
export default function ManageVideos() {
const { videos, addVideo, updateVideo, deleteVideo } = useVideos("");
const [videoList, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
dispatch({
type: "videos",
value: videos,
});
}, [videos]);
const columns = [
{ title: "Serial", field: "sl" },
{ title: "Title", field: "title" },
{ title: "Number of Qusetion", field: "noq" },
{ title: "Youtube Video Id", field: "youtubeID" },
];
return (
<div>
<MaterialTable
title="Videos Table"
data={videoList}
columns={columns}
editable={{
onRowAdd: (newData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
addVideo(newData);
resolve();
}, 1000);
}),
onRowUpdate: (newData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
updateVideo(newData);
resolve();
}, 1000);
}),
}}
/>
</div>
);
}
Since the information provided is a bit lacking, I'll assume that the useEffect hook is not working when you update your videos (check it with consle.log("I'm not working") and if it does work then you can just ignore this answer).
You can define a simple state in this component, let's call it reRender and set the value to 0, whenever the user clicks on the button to add a video you should call a function which adds 1 to the value of reRender (()=>setReRender(prevState=>prevState+1)). In your useEffect hook , for the second argument pass reRender. This way, when the user clicks to submit the changes , reRender causes useEffect to run and dispatch to get the new data.
If this doesn't work , I have a solution which takes a bit more work. You will need to use a state manager like redux or context api to store your state at a global level. You should store your videos there and use 1 of the various ways to access the states in this component (mapStateToProps or store.subscribe() or ...). Then pass the video global state as the second argument to useEffect and voilĂ , it will definitely work.

updating object inside array inside object using prevState and the useState hook

I'd like to remove a nested object based on the id is equal to a passed prop. At the moment, the entire object is replaced. I'm missing something, when trying to update the state using useState probably with the way I'm looping my object?
UPDATE: The question was closed in response to available answers for updating nested objects. This question involves arrays which I believe are part of the issue at hand. Please note the difference in nature in this question with the forEach. Perhaps a return statement is required, or a different approach to the filtering on id..
my initial object looks like this:
{
"some_id1": [
{
"id": 93979,
// MORE STUFF
},
{
"id": 93978,
// MORE STUFF
}
],
"some_id2": [
{
"id": 93961,
// MORE STUFF
},
{
"id": 93960,
// MORE STUFF
}
]
}
and I go through each item as such:
for (const key in items) {
if (Object.hasOwnProperty.call(items, key)) {
const element = items[key];
element.forEach(x => {
if (x.id === singleItem.id) {
setItems(prevState => ({
...prevState,
[key]: {
...prevState[key],
[x.id]: undefined
}
}))
}
})
}
There are 3 problems in your code:
You are setting the value of key to an object while the items is expected to have an array to ids.
// current
[key]: {
...prevState[key],
[x.id]: undefined
}
// expected
[key]: prevState[key].filter(item => item.id === matchingId)
If you intend to remove an object from an array based on some condition, you should be using filter as pointed out in Owen's answer because what you are doing is something else:
const a = { xyz: 123, xyz: undefined };
console.log(a); // { xyz: undefined} - it did not remove the key
To make your code more readable, it is expected to manipulate the entire object items and then, set it to the state once using setItems - in contrast to calling setItems multiple times inside a loop and based on some condition.
This makes your code more predictable and leads to fewer re-renders.
Also, the solution to your problem:
// Define this somewhere
const INITIAL_STATE = {
"some_id1": [
{
"id": 93979
},
{
"id": 93978
}
],
"some_id2": [
{
"id": 93961
},
{
"id": 93960
}
]
};
// State initialization
const [items, setItems] = React.useState(INITIAL_STATE);
// Handler to remove the nested object with matching `id`
const removeByNestedId = (id, items) => {
const keys = Object.keys(items);
const updatedItems = keys.reduce((result, key) => {
const values = items[key];
// Since, we want to remove the object with matching id, we filter out the ones for which the id did not match. This way, the new values will not include the object with id as `id` argument passed.
result[key] = values.filter(nestedItem => nestedItem.id !== id)
return result;
}, {});
setItems(updatedItems);
}
// Usage
removeByNestedId(93961, items);
Probably a simple reduce function would work, Loop over the entries and return back an object
const data = {"some_id1": [{"id": 93979},{"id": 93978}],"some_id2": [{"id": 93961},{"id": 93960}]}
const remove = ({id, data}) => {
return Object.entries(data).reduce((prev, [nextKey, nextValue]) => {
return {...prev, [nextKey]: nextValue.filter(({id: itemId}) => id !== itemId)}
}, {})
}
console.log(remove({id: 93961, data}))
your way solution
for (const key in items) {
if (Object.hasOwnProperty.call(items, key)) {
const element = items[key];
element.forEach(x => {
if (x.id === singleItem.id) {
setItems(prevState => ({
...prevState,
//filter will remove the x item
[key]: element.filter(i => i.id !== x.id),
}))
}
})
}
}
short solution.
for(const k in items) items[k] = items[k].filter(x => x.id !== singleItemId);
const items = {
"some_id1": [
{
"id": 93979,
},
{
"id": 93978,
}
],
"some_id2": [
{
"id": 93961,
},
{
"id": 93960,
}
]
}
const singleItemId = 93979;
for (const k in items) items[k] = items[k].filter(x => x.id !== singleItemId);
console.log(items);
//setItems(items)
You could try using the functional update.
const [data, setData] = [{id:1},{id:2},{id:3}...]
Once you know the id which you need to remove.
setData(d=>d.filter(item=>item.id !== id));

change of one value in the state react

In the state I have data array that look like this
{
"offers" : [
{
"model": "shoes",
"availability": false,
"id": 1
},
{
"model": "t-shirt",
"availability": true,
"id": 2
},
{
"make": "belt",
"availability": false,
"id": 3
}
]
}
My task is to change the accessibility field. I have a button - change the availability. After clicking it I want to make the availability of a single field changed
changeAvailability = (ailability, id) => {
console.log(ailability)
console.log(id)
const { data } = this.state;
let newData = [...data]
this.setState({
data: newData.map(x => x.id === id ? x.ailability = !ailability : ailability )
})
}
I have created a function that does not work.
My idea was: I click on the element, pass to the element's id and accessibility. In this function, I create a copy of the state. In the right place, I change the availability and I am sending everything to the state.
This function does not work, nothing happens, I have no idea how to fix it
Seems like the map function is returning nothing ...
Could be something like this
changeAvailability = (ailability, id) => {
console.log(ailability)
console.log(id)
const { data } = this.state;
let newData = [...data]
const updateItem = (x) => {
if (x.id === id) {
x.ailability = !ailability;
}
return x;
};
this.setState({
data: newData.map(updateItem)
})
}
I am assuming you want to click on an element which passes an id and availability of an offer, find a matching entry (on the passed id) in the offers array in the state and change the availability of the found object to what you passed in the function.
I recommend using the callback that this.setState() allows you to use, providing a reference to a copy of the previous state, before the setState was called.
changeAvailability = (passedAvail, passedID) => {
this.setState((prevState) => {
// Make a copy of the offers array in the state
const offersArray = prevState.offers
// Find index of matching obj by id
const matchObjIndex = offersArray.findIndex(
(offerObj) => (
offerObj.id === passedID
)
)
// If a match was indeed found
if (matchObjIndex > -1) {
// Find matching obj from index obtained
const matchObj = offersArray[matchObjIndex]
// Change the availibility of the matched obj and obtain a modified obj
const modmatchObj = Object.assign(matchObj, {
availability: passedAvail
})
// Replace the offer obj by index in the offers array.
offersArray.splice(matchObjIndex, 1, modmatchObj);
}
// Finally set state with the updated offers lists
// If no match was found, it will return the old offers array
return {offers: offersArray }
})
}
You can try something like this to make it work:
changeAvailability = (availability, id) =>
this.setState(prevState => {
return {
offers: prevState.offers.map((e, i) =>
e.id !== id ? e : { ...prevState.offers[i], availability }
)
};
});
The issue with your solution is that you shouldn't modify the items you're iterating while doing a map, but you should just return whatever new data you wanna add.

Resources