I am currently struggling displaying the contents of my database in my front end components. I am using the MERN stack to create a grocery list app and so far, I have a form with an onsubmit event in my front end that is sending data to MongoDB and saving it. I see what I am submitting in my database without a problem.
To display the data I have created a backend route where I find the list in my database
backend route image
then I make an api to that backend route via a gentlest action I create
getlist api action
I try calling his action in my front end component but all I see is an empty array in my console not a populated list of any sort. In the past I make the api call then set the state of whatever I need to set and map through that to display it . Let me know if you guys have any ideas
frontend component to show the list
Usually the way you communicate with your backend api is by performing a request via axios or fetch.
If you use axios then create a function and perform a post request to your backend route inside it. Then call that function inside useEffect.
If you are using redux then simply call the action inside useEffect.
Edit:
Let's assume that your server is listening to port 8000 for this example
`
const [groceryList, setGroceryList] = useState([]);
async function fetchData() {
const result = await axios.get(`/http://localhost:8000/getList`);
setGroceryList(result);
}
useEffect(() => {
fetchData()
},[])
`
All You Need To Do:
import {useEffect,useState } from "react";
//use it inside your function component in JS file and make sure your //server 3001 is runing by using express
function abcd(){
const [object, setObj] = useState([
{
title: "",
description: "",
url: "",
},
]);
useEffect(() => {
fetch("http://localhost:3001/pages")
.then((res) => res.json())
.then((jsonRes) => setObj(jsonRes));
}, []);
//Render
return(
<div
style={{
display: "flex",
flexDirection: "column",
width: "20%",
margin: "auto auto",
}}
>
{object
.sort((a, b) => {
var titleA = a.title;
var titleB = b.title;
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
return 0;
})
.map((Ob) => {
return (
<img
key={Ob.title}
src={Ob.url}
alt={Ob.description}
name={Ob.title}
onClick={handleClick}
></img>
);
})}
</div>)
}
//Use the below code in your BackEnd in order to fetch all the data from the //MongoBackEnd(Express)
//Use your MongoDB server address and mongoSchema and store it in a //Variable("Handle")
const Handle = require("./views/mongoschema");
app.get("/pages", (req, res) => {
Handle.find({})
.then((items) => res.json(items))
.catch((err) => console.log(err));
});
By doing the above things you can fetch all the MongoDB data in the frontEnd and add designs or Templates to it.
Related
In my database, there are files. every single file has 'IS_DELETE' row, which shows whether this file is deleted or not.
If 'IS_DELETE' row of a file is 0, it means it is not deleted by the writer of the post.
If 'IS_DELETE' row of a file is 1, it means it is deleted by the writer.
However, I use a GET request to render the items.
I made the code so that you can see it clearly. I might mispelled!
test = () => {
const [files, setFiles] = useState([]);
const getDetails = async () => {
await get(`URL`)
.then((res) => {
setFiles(res)
})
}
useEffect=(() => {
getDetails()
},[])
return (
<>
files.map((file) => (
<div>
{file}
</div>
))
</>
)}
With this code, I render every file, although some files have '1' in their IS_DELETE row.
I am wondering whether I can conditionally get the items from DB
or
I get every file and filter my array using a particular function.
FYI, I get the files in this format.
[
{PK:1, NAME:'Hello.jpg', IS_DELETE: 0}
{PK:2, NAME:'Nice.jpg', IS_DELETE: 1}
{PK:3, NAME:'To.jpg', IS_DELETE: 0}
]
Thank you. I'm not good at English. So please be understandable.
Waiting for your wisdom!
No, you cannot modify the response you get from server unless it specifically has an endpoint to do so. It's like sending a letter and awqiting the response - you cannot make the response diffeent unless your recipient allows you to send a letter with a specific request (eg. 'give me only non-deleted items'), but if you're not in charge of the server, the only thing you can do is indeed filtering the items you get in response.
const getDetails = async () => {
await get(`URL`)
.then((res) => {
const filtered = res.filter(file => file.IS_DELETE === 0);
setFiles(filtered);
})
}
you can pass the parameter is_deleted in the url concatenate with the main one
{url}?is_deleted=0
Depending on the database you are using, it is possible to do so. For example, in an SQL database, you could do SELECT * FROM files WHERE IS_DELETE != 0
But you can also do it in the frontend as shown below:
const getDetails = async () => {
const res = await get(`URL`);
setDetails(res.filter(file => !file.IS_DELETE));
}
my idea is to pass parameter in the url as query string and in the server side you get the value and put it in the database query
test = () => {
const [files, setFiles] = useState([]);
const getDetails = async () => {
await get(`URL?is_deleted=0`)
.then((res) => {
setFiles(res)
})
}
useEffect=(() => {
getDetails()
},[])
return (
<>
files.map((file) => (
<div>
{file}
</div>
))
</>
)}
I'm currently working on a search functionality in React Native using axios.
When implementing search functionality i'm using debounce from lodash to limit the amount of requests sent.
However, since request responses are not received in same order there is a possibility of displaying incorrect search results.
For example when the user input 'Home deco' in input field there will be two requests.
One request with 'Home' and next with 'Home deco' as search query text.
If request with 'Home' takes more time to return than second request we will end up displaying results for 'Home' query text not 'Home deco'
Both results should be displayed to the user sequentially, if responses are returned in order but if 'Home' request is returned after 'Home deco' request then 'Home' response should be ignored.
Following is a example code
function Search (){
const [results, setResults] = useState([]);
const [searchText, setSearchText] = useState('');
useEffect(() => {
getSearchResultsDebounce(searchText);
}, [searchText]);
const getSearchResultsDebounce = useCallback(
_.debounce(searchText => {
getSearchResults(searchText)
}, 1000),
[]
);
function getSearchResults(searchText) {
const urlWithParams = getUrlWithParams(url, searchText);
axios.get(urlWithParams, { headers: config.headers })
.then(response => {
if (response.status === 200 && response.data)
{
setResults(response.data);
} else{
//Handle error
}
})
.catch(error => {
//Handle error
});
}
return (
<View>
<SearchComponent onTextChange={setSearchText}/>
<SearchResults results={results}/>
</View>
)
}
What is the best approach to resolve above issue?
If you want to avoid using external libraries to reduce package size, like axios-hooks, I think you would be best off using the CancelToken feature included in axios.
Using the CancelToken feature properly will also prevent any warnings from react about failing to cancel async tasks.
Axios has an excellent page explaining how to use the CancelToken feature here. I would recommend reading if you would like a better understanding of how it works and why it is useful.
Here is how I would implement the CancelToken feature in the example you gave:
OP clarified in the replies that they do not want to implement a cancelation feature, in that case I would go with a timestamp system like the following:
function Search () {
//change results to be a object with 2 properties, timestamp and value, timestamp being the time the request was issued, and value the most recent results
const [results, setResults] = useState({
timeStamp: 0,
value: [],
});
const [searchText, setSearchText] = useState('');
//create a ref which will be used to store the cancel token
const cancelToken = useRef();
//create a setSearchTextDebounced callback to debounce the search query
const setSearchTextDebounced = useCallback(
_.debounce((text) => {
setSearchText(text)
), [setSearchText]
);
//put the request inside of a useEffect hook with searchText as a dep
useEffect(() => {
//generate a timestamp at the time the request will be made
const requestTimeStamp = new Date().valueOf();
//create a new cancel token for this request, and store it inside the cancelToken ref
cancelToken.current = CancelToken.source();
//make the request
const urlWithParams = getUrlWithParams(url, searchText);
axios.get(urlWithParams, {
headers: config.headers,
//provide the cancel token in the axios request config
cancelToken: source.token
}).then(response => {
if (response.status === 200 && response.data) {
//when updating the results compare time stamps to check if this request's data is too old
setResults(currentState => {
//check if the currentState's timeStamp is newer, if so then dont update the state
if (currentState.timeStamp > requestTimeStamp) return currentState;
//if it is older then update the state
return {
timeStamp: requestTimeStamp,
value: request.data,
};
});
} else{
//Handle error
}
}).catch(error => {
//Handle error
});
//add a cleanup function which will cancel requests when the component unmounts
return () => {
if (cancelToken.current) cancelToken.current.cancel("Component Unmounted!");
};
}, [searchText]);
return (
<View>
{/* Use the setSearchTextDebounced function here instead of setSearchText. */}
<SearchComponent onTextChange={setSearchTextDebounced}/>
<SearchResults results={results.value}/>
</View>
);
}
As you can see, I also changed how the search itself gets debounced. I changed it where the searchText value itself is debounced and a useEffect hook with the search request is run when the searchText value changes. This way we can cancel previous request, run the new request, and cleanup on unmount in the same hook.
I modified my response to hopefully achieve what OP would like to happen while also including proper response cancelation on component unmount.
We can do something like this to achieve latest api response.
function search() {
...
const [timeStamp, setTimeStamp] = "";
...
function getSearchResults(searchText) {
//local variable will always have the timestamp when it was called
const reqTimeStamp = new Date().getTime();
//timestamp will update everytime the new function call has been made for searching. so will always have latest timestampe of last api call
setTimeStamp(reqTimeStamp)
axios.get(...)
.then(response => {
// so will compare reqTimeStamp with timeStamp(which is of latest api call) if matched then we have got latest api call response
if(reqTimeStamp === timeStamp) {
return result; // or do whatever you want with data
} else {
// timestamp did not match
return ;
}
})
}
}
I am currently trying to connect my shopify account to react using js-buy-sdk.
I can get my products calling fetchByHandle after buildClient in same component. However, I want to use my client returned from buildClient in many different components. Do I need to call buildClient in every component? I tried to use redux for defining global state to client in App.js but subcomponents are not waiting for the buildClient function completes and are giving client.product.fetchByHandle is not a function.
This is my code (ProductPage component to fetch product):
const client = useSelector(state => state.client);
useEffect(() => {
if(client && client.product) {
client.product.fetchByHandle('any-handle').then(product => {
setProduct(product);
});
}
}, [client]);
App.js
useEffect(() => {
const client = Client.buildClient({
domain: 'xyz.myshopify.com/',
storefrontAccessToken: 'token'
});
dispatch(setClient(client));
client.product.fetchAll().then((products) => {
console.log(products);
dispatch(getProducts(products));
});
}, [dispatch]);
I'm trying to consume a json response from an Express style server in Firebase's Cloud functions, but I'm unable to present the response in the DOM. The response path and status (200) are good, but the response data I'm getting in the browser is my entire index.HTML page, not the json data.
Here's my basic set up in the cloud functions:
app.get("/tester", (req, res) => {
res.json({ serverData: "Hello" });
});
exports.app = functions.https.onRequest(app);
and my React FE code to consume it:
function App() {
let I;
const onClick = () => {
axios.get("/tester").then(res => {
I = res.data.serverData;
console.log(I);
});
};
return (
<div>
<button onClick={onClick}>click</button>
<div>{I}</div>
</div>
);
}
Like I said above, the response data I'm getting in the dev tools is just the barebones index.html page, not the text I want to receive. How can I map this data to the DOM?
You need I to be defined as a 'state' property within your function. Easiest way to do that is use React's useState hook. First add this to your script by including:
import React, { useState } from 'react';
Then declare I using the useState hook and, after retrieving the data, use the associated setter to set the new value:
function App() {
let [I, setI] = useState('');
const onClick = () => {
axios.get("/tester").then(res => {
setI(res.data.serverData);
console.log(I);
});
};
return (
<div>
<button onClick={onClick}>click</button>
<div>{I}</div>
</div>
);
}
I have an application with React as client framework and Graphql for running the API.
Plan is fetching user posts and posts count.
Client side
const Profile = () => (
<div>
<PostCount />
<Posts />
</div>
)
const PostCount = () => ...
const Posts = () => ...
We need to display posts in Posts component and the posts count in Count component.
So my question is, which is better.
fetch all of data in Profile component in one request and send it to Post and Count component as props. or fetch posts count in Count component and posts in Post component.
scenario one includes one request to server and bigger chunk of data.
scenario two includes two request to server and smaller chunk of data.
Scenario one server side:
const schema = `
type Query {
feeds(): Feed!
}
type Feed {
posts: [Post!]!
count: Int!
}
type Post {
...
}
`
async function feed() {
const posts: await Post.findAll();
const count = await Post.count();
return {
count
posts,
}
}
Scenario two server side:
const schema = `
type Query {
posts(): [Post!]!
count(): Int!
}
type Post {
...
}
`
async function feed() {
const posts: await Post.findAll();
return posts;
}
async function count() {
const count = await Post.count();
return count;
}
P.S. also consider bigger data. posts and counts are example. for example user posts and user comments.
Both ways are correct! it depends on your application to choose a better approach!
count is usually faster than fetching data ( someone might mess it up!:D), so if you fetch it separately, you can show it faster while your posts are still in loading.
BTW, GQL handles Promise by itself! so there's no need for that async awaits!