Fetching , rendering and sorting data in Reactjs - reactjs

How can i fetch data from a json file and render them in an ascending order? For example let´s say i have this json file
[{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}
and i want to render the data with only Id and name in ascending order.
I tried coding it in different ways but since i am still a beginner i can´t really figure it out. Thank you so much

You can use .map and .sort:
const data = [{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}]
const sorted = data.map(d => ({id: d.id, name: d.name})).sort((el1, el2) => el1.id - el2.id)
console.log(sorted)
Once you set the new sorted and mapped array to a state variable, you can map over it to render.
Example
//Functional Component:
const Example = ({data}) => {
const [sortedData, setSortedData] = useState([])
useEffect(() => {
if(data) setSortedData(data
.map(d => ({id: d.id, name: d.name}))
.sort((el1, el2) => el1.id - el2.id)))
}, [data])
return(
sortedData?.map(element => <div key={element.id}> {element.name} </div>)
)
}

You can short your array data using the .sort method.
App.js
import React, { useState, useEffect } from "react";
import Data from "./data.json";
function App() {
const [data, setData] = useState();
const sortDataByField = (array, field) => {
return array.sort((a, b) =>
a[field].toString().localeCompare(b[field].toString())
);
};
useEffect(() => {
const sortData = sortDataByField(Data, "name");
setData(sortData);
}, []);
return (
<div className="App">
{data?.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
export default App;
data.json
[{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}]

Import and set up a useState hook
import React, {usesState} from 'react'
const [companies, setCompanies] = useState([])
fetch the data
const data = fetch('/api/v1/companies', {method: 'GET'})
sort first to order, then map to only get id and name
const sorted = data.sort((a, b)=>{return a.name > b.name ? 1 : -1})
.map(({id, name}) => {return {id, name}})
/* this return only id and name sorted by name
*
* if you wanna sort by id, do this:
* .sort((a, b) => a.id - b.id)
*/
When you done sorting and mapping, store it to that useState hook so that you can render it:
setCompanies(sorted)
Then render it, also prevent errors by checking if you state has data before rendering:
companies &&
companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
// or
companies
? companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
: <p>No data</p>
All code
import React, {usesState, useEffect} from 'react'
export default App = () =>{
const [companies, setCompanies] = useState([])
useEffect(()=>{
fetchData()
}, [])
const fetchData = async () => {
try{
const data = fetch('/api/v1/companies', {method: 'GET'})
if(data){
const sorted = data.sort((a, b)=>{return a.name > b.name ? 1 : -1})
.map(({id, name}) => {return {id, name}})
setCompanies(sorted)
}
else{
console.log('No data')
}
}
catch (err){
console.log(err)
}
}
return(
<div>
companies &&
companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
</div>
)
}

Related

Why my React Query does not instant update UI after delete?

The deleted Todo item is still on display after clicking the delete button.
It does not immediately remove on display, but on my db.json file it shows that it has been deleted.
I also test by placing the component inside App.jsx and there was no problem everything works fine but when I nested the component the delete function works yet it does not immediately update
I'm using
json-server,
react vite,
react query
import React from "react";
import { useQuery, useMutation, QueryClient } from "#tanstack/react-query";
import axios from "axios";
export const axiosClient = axios.create({
baseURL: "http://localhost:8500",
});
const queryClient = new QueryClient();
const SingleTask = ({ listId }) => {
const { data: taskTodo } = useQuery(
["tasks", listId],
async () => (await axiosClient.get(`/tasks/${listId}/subtasks`)).data,
{
initialData: [],
}
);
const deleteTask = useMutation(
({id}) => axiosClient.delete(`/subtasks/${id}`),
{
onSettled: () => queryClient.invalidateQueries(["tasks"])
}
);
return (
<>
{taskTodo
?.filter((entry) => entry.status != true)
.map((list) => (
<React.Fragment key={list.id}>
<div className="mt-6">
<div className="flex justify-between items-center text-sm">
<div className="flex gap-2">
<p>{list.title}</p>
</div>
<div className="flex gap-4">
<button onClick={() => {
deleteTask.mutate(list);
}}>
Delete
</button>
</div>
</div>
</div>
</React.Fragment>
))}
</>
);
};
export default SingleTask;
Here is the json data of tasks and subtask
json data
{
"tasks": [
{
"id": 1,
"status": false,
"title": "List One",
"details": ""
},
{
"id": 2,
"status": false,
"title": "List Two",
"details": ""
}
],
"subtasks": [
{
"id": 1,
"taskId": 1,
"status": false,
"title": "Subtask for list one",
"details": ""
},
{
"id": 2,
"taskId": 2,
"status": true,
"title": "Subtask for list two",
"details": ""
}
]
}
That's because you're invalidating your query. After the query gets invalidated, it will do a refetch of your active query (that is ["tasks", listId], unless you specify otherwise). So you have to wait for the refetch to complete in order to see the update, thus it is not immediate.
If you want it to be "immediate" and if you know what the state will look like, you can use optimistic updates for that.
In your case it could be something like this:
const queryClient = useQueryClient()
useMutation(({id}) => axiosClient.delete(`/subtasks/${id}`), {
// When mutate is called:
onMutate: async ({id}) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries(['tasks', id])
// Snapshot the previous value
const previousTasks = queryClient.getQueryData(['tasks', id])
// Optimistically update to the new value
queryClient.setQueryData(['tasks', id], old => old.filter((t) => t.id !== id))
// Return a context object with the snapshotted value
return { previousTasks }
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (err, { id }, context) => {
queryClient.setQueryData(['tasks', id], context.previousTasks)
},
// Always refetch after error or success:
onSettled: (newData, error, { id }) => {
queryClient.invalidateQueries(['tasks', id])
},
})
UPDATE
Since your query keys are dependant on list.listIds and not list.ids (like I assumed), you would need to update your useMutation function to something like this:
const queryClient = useQueryClient();
useMutation(({ id }) => axiosClient.delete(`/subtasks/${id}`), {
onMutate: async ({ listId, id }) => {
await queryClient.cancelQueries(['tasks', listId]);
const previousTasks = queryClient.getQueryData(['tasks', listId]);
queryClient.setQueryData(['tasks', listId], (old) => old.filter((t) => t.id !== id));
return { previousTasks };
},
onError: (err, { listId }, context) => {
queryClient.setQueryData(['tasks', listId], context.previousTasks);
},
onSettled: (newData, error, { listId }) => {
queryClient.invalidateQueries(['tasks', listId]);
},
});

Nested array with React and Firestore - How to display?

I have a nested array within the individual items in a collection.
{
"id": "RpFRcKLIgELlBLgIOJM4",
"Category": "",
"Method": "",
"User": "rWFZhAKk9eOSIIFoP0DqqvrC6WJ3",
"Foods": [
{
"Weight": 1.065,
"label": "Milk - Long Life (1 Litre) (1.065)",
"value": "Milk-LongLife(1Litre)"
},
{
"label": "Blueberries (0.125)",
"value": "Blueberries",
"Weight": 0.125
}
],
"Name": "456",
"Serves": ""
}
{
"id": "WQ6KBLevFsCdV73j4KU4",
"Category": "",
"Name": "123",
"Foods": [
{
"value": "Wine-White",
"label": "Wine - White"
},
{
"value": "Milk-LongLife(1Litre)",
"label": "Milk - Long Life (1 Litre)"
}
],
"Serves": "",
"User": "rWFZhAKk9eOSIIFoP0DqqvrC6WJ3",
"Method": ""
}
const useItemsMeals = () => {
const user = firebase.auth().currentUser;
const user1 = user.uid;
const [items, setItems] = useState([]); //useState() hook, sets initial state to an empty array
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("meals")
.where("User", "==", user1)
.orderBy("Category", "asc")
.onSnapshot(snapshot => {
const listItemsMeals = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setItems(listItemsMeals);
console.log(listItemsMeals);
});
return () => unsubscribe();
}, []);
return items;
};
I am having a tough time trying to display items from the 'Foods' array, am currently using for my return:
const listItemMeals = useItemsMeals();
{listItemMeals.map(item => (
<TableRow hover key={item.id} id={item.id}>
<TableCell>{item.Category}</TableCell>
<TableCell>{item.Name}</TableCell>
<TableCell>{item.Foods}</TableCell>
When doing this it tells me:
Error: Objects are not valid as a React child (found: object with keys {label, value}). If you meant to render a collection of children, use an array instead.
I think I need to map this nested array again somehow - but for the life of me - cannot figure it out!
You're almost there.
Your useItemsMeals functions loads the data from Firestore, and sets it correctly into the items variable in the state to render it. But then you use const listItemMeals = useItemsMeals() in your rendering code, which messes things up.
You should not try to return any value from useItemsMeals, and instead solely rely on the items variable from the state to pass the information between the database and the rendered.
So:
// return items; 👈 remove this
---
// const listItemMeals = useItemsMeals(); 👈 remove this
---
{items.map(item => ( // 👈 read items from the state instead
You need to loop over the Foods array again. Like this
const listItemMeals = useItemsMeals();
{listItemMeals.map(item => (
<TableRow hover key={item.id} id={item.id}>
<TableCell>{item.Category}</TableCell>
<TableCell>{item.Name}</TableCell>
{
item.Foods.map(food=>(
<div> //this can div or a new row tag
<TableCell>{food.weight}</TableCell>
<TableCell>{food.label}</TableCell>
<TableCell>{food.value}</TableCell>
</div>))
}

filter array in props

i keep having a problem with filtering an array of objects in props in a nextjs page. i m using below json and coding.
[
{
"id": "1",
"name": "name1",
"category": ["food"]
},
{
"id": "2",
"name": "name2",
"category": ["food", "beverages"]
}]
import React from "react";
const test2 = ({ prods }) => {
return (
<div>
<div>
{prods
.filter((product) => product.category.includes("eve"))
.map((filterarray) => (
<li>
{filterarray.id}
{filterarray.name}
{filterarray.category}
</li>
))}
</div>
</div>
);
};
export async function getStaticProps() {
const prods = (await import("./product.json")).default;
return {
props: {
prods,
},
};
}
export default test2;
listing the full array works. (data comes as 1 string eg 'foodbeverage' but that should still be ok i think)
Filtering on id works fine. But when i try to use include it no longer displays any result.
if anyone could point me to what i m doing wrong. or if i better follow a different approach, any help would be much appreciated.
You need to replace include with some on the category array:
let prods = [
{
id: "1",
name: "name1",
category: ["food"]
},
{
id: "2",
name: "name2",
category: ["food", "beverages"]
}
];
let result = prods.filter((product) =>
product.category.some((pro) => pro.includes("eve"))
);
console.log(result);
Array#includes() does not do partial matches. You need to use String#includes() on each element in the array.
You could do this by using Array#some() in the filter.
prods.filter((product) => product.category.some(cat=>cat.includes("eve")))

Mapping API response to JSX

I have a JSON response in the form:
[
{
"author": 2,
"title": "how to draw",
"slug": "how-to-draw",
"content": "second attempt",
"status": 0,
"date_created": "2020-11-28T20:25:41.650172Z",
"date_posted": "2020-11-28T20:25:41.650172Z",
"tags": "none"
},
{
"author": 1,
"title": "Admin test post",
"slug": "admin-test-post",
"content": "This is just a test of the content field!\r\n\r\nParagraph 2",
"status": 4,
"date_created": "2020-11-16T21:02:02.521705Z",
"date_posted": "2020-11-16T21:37:40.477318Z",
"tags": "a b c"
}
]
And I am struggling to map the response correctly to a series of divs in ReactJS. The idea would be to have a div for each post, each with the author, title, etc. of the post. I can use post[0].title to take the title of the first element of the array. Am I missing something obvious?
import React, {useState} from 'react';
import axios from 'axios';
const Django = () => {
const [posts, setPosts] = useState([]);
// proxy set to http://127.0.0.1:8000/
const apiLink = "/api/post/";
const fetchData = async () => {
const res = await axios.get(`${apiLink}`, { headers: { Accept: "application/json"} });
console.log(res.data);
setPosts([res.data]);
}
return (
<div>
<h1>Posts:</h1>
<button onClick={fetchData}>Load jokes</button>
{posts.map((post, key) => {
//console.log(joke);
return (
<div className="data" key={key}>
{post.title}
{key}
</div>
)})}
</div>
);
}
export default Django;
It is not exactly clear what your problem is, but I suppose you're confused about why you aren't getting any data by directly calling {post.title} in the map body. That's because you're putting the result from your fetch, which is already an array into another array: setPosts([res.data]);. Just change the state setter to setPosts(res.data); and everything in the map should work fine.

how do I Mock API and following the same approach as my static array?

I have a React hooks component, which uses an HTML div-alike table to render data, and the data is being fetched from the server. I need to test the component to see if the table has data by mocking the API call. Below is the current code. I want to remove the use of arr completely and make avoid getting {users} rendered as text/plain as you can you in the below image
const arr = [
{
"demo": [
{
"_id": "T0810",
"title": "Historian",
"tags": [
"demo"
],
"queries": [],
},
{
"_id": "T0817",
"title": "book",
"tags": [
"demo"
],
"queries": [],
},
],
"demo_2": [
{
"_id": "T0875",
"title": "Program",
"tags": [
"demo_2",
"Control"
],
"queries": [],
},
{
"_id": "T0807",
"title": "Interface",
"tags": [
"demo_2"
],
"queries": [],
}
]
}];
const keys = Object.keys(arr[0]);
export default function Demo () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://run.mocky.io/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e');
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/>}
</div>
);
}
Second attempt...Assuming I understand the ask then a package like Nock can do the trick (https://github.com/nock/nock).
The approach is to allow Nock to mock the API and return the result locally without calling the server. In your case it will be something like:
const nock = require('nock');
const arr = [...]; // your original definition of arr
const scope = nock('https://run.mocky.io')
.get('/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e')
.reply(200, arr);
// The rest of your code "as is"
...
This setup of Nock will intercept every HTTP call to https://run.mocky.io and will specifically return the content of arr in response to a GET /v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e.
Typically, I would not put this code in the main code.
Instead you can use it as part of a testing script that runs without dependency on an active server. E.g. testing during build.

Resources