I want to implement button behavior Show more.
When I click the button, new objects must be loaded without rebooting.
I do const [data, setData] = useState(users); to initialize the first 10 users.
When I press the button, that to this array data, I add the following 10 users
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
But nothing is rendered in the browser.
how to do it correct?
const Cards = ({users, bottomUrl }) => {
const [data, setData] = useState(users);
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
var users = response.data;
for (var i=0; i < users.length; i++) {
data.push(users[i]);
}
setData(data);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};
return (
<div>
<div className="users-list">
{data.map((element, elIndex) => (
<UserCard
key={elIndex}
lastName={element.lastName}
firstName={element.firstName}
description={element.description}
/>
))}
</div>
<div className="users-page-button-container">
<a className="button" onClick={handleSubmit} href={bottomUrl}>Show more</a>
</div>
</div>
)
};
You are pushing on the state object data. You must use an intermediate variable and pass that to setData
You are manipulating the state directly, which is wrong. Please update the handleSubmit method as below.
const handleSubmit = e => {
e.preventDefault();
const page = bottomUrl.slice(-1);
const axios = require('axios');
const url = '/users';
axios.get(url, {
params: { page: page }
}, {headers: {'Content-Type': 'application/json'}})
.then(function (response) {
users = response.data;
setData([...data, ...users]); // You need to save the previous data along with the new data and pass it together to setData function. So here data = previous data + new users data
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
};
Related
I'm getting a 400 client error and saying state.category.push is not a function and the current state is pending. I will be happy if someone helps me out on this. The category array is not accepting the data coming into it. I have both the form file and the reducer file down there. I am trying to create a category array to accepts users category and later output it out.
blogSlice.js
My Reducer file
const urlParams = new URLSearchParams(window.location.search)
const TokenAuthless = urlParams.get('enter')
if(TokenAuthless){localStorage.setItem('authless',
JSON.stringify(TokenAuthless))}
var Token = JSON.parse(localStorage.getItem("authless"))
const initialState = {
blogItems: [],
isLoading: null,
category: [{}],
authors: [],
}
const categoryUrl = 'api/v1/admin/createBlogCat';
var api_origin = 'xxxxxxxxxxxx'
export const insertCategory = createAsyncThunk('blog/insertCategory', async(data,
thunkAPI) => {
const{ rejectWithValue } = thunkAPI;
try {
const res = await fetch(`${api_origin}/${categoryUrl}`,{
method :'POST',
mode: 'cors',
body: JSON.stringify({data}),
headers : {
'Authorization': `Bearer ${Token}`,
'Content-type':'application/json',
'Accept':'application/json',
'Access-Control-Allow-Origin':'*',
},
})
const catData = await res.json();
return catData.data;
} catch (error) {
return rejectWithValue(error.message)
}
})
[insertCategory.pending]:(state, action) => {
state.isLoading = true;
},
[insertCategory.fulfilled]: (state, action) => {
state.isLoading = false;
console.log(action.payload);
console.log(current(state))
state.category.push(action.payload);
console.log('the state category', state.category);
},
[insertCategory.rejected]:( state, action ) => {
state.isLoading = false;
},
CreateCategory.js
Creating a form to accept the input here
const CreateCatAut = () => {
const [name, setName] = useState('');
const dispatch = useDispatch()
const handleSubmit=(e)=>{
e.preventDefault();
const categoryData = {name}
dispatch(insertCategory(categoryData));
console.log(categoryData)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Active Input"
value={name}
onChange={(e)=> setName(e.target.value)} />
)
It means that your state.category is not an array. At
state.category.push(action.payload)
I assigned a value to the state.category which is not yet an array. By first creating an array and then putting that element into it.
state.category= [action.payload]
and at the next iterations, i will have an array with one element and can use push on it.
I cannot figure out for the life of me why the first pair of onDoubleClick fires 500 server error but on all subsequent onDoubleClicks, it fires 200 correctly but with inconsistent behavior depicted below.
For illustration purposes, this is what's happening:
double click on image A for user ID 1 -> 500 error
double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
double click on image B for user ID 2 -> 200 success (updates successfully for user ID 1 when it should've been updated for user ID 2)
and so on....
I've checked all over SO, even tried all of the suggestions on the most popular question asked on this topic on SO but to no avail. I've hit a brick wall.
I believe it has something to do with the asynchronous nature of hooks and how I'm defining them but I'm not entirely sure.
How can I make it so that upon double clicking any image, the corresponding data gets sent to the server correctly on the first pair of double clicks as well as any subsequent pair double clicks (i.e. on a different image)?
For illustration purposes, this how I'd like it to behave:
double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
double click* on image b for user ID 2 -> 200 success (updates successfully for user ID 2)
and so on....
const Images = () => {
let authToken = localStorage.getItem("token");
const [uploadsData, setUploadsData] = useState([]);
const [likedPhotoUserId, setLikedPhotoUsedId] = useState("");
const [like, setLike] = useState(0);
useEffect(() => {
getUploads();
}, []);
useEffect(() => {}, [uploadsData]);
const getUploads = () => {
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
axios
.get("http://localhost:8005/api/get-user-uploads-data", { headers })
.then((resp) => {
console.log(resp.data);
setUploadsData(resp.data);
})
.catch((error) => {
console.log(error);
});
};
const handleLikesBasedOnUserId = (e) => {
setLikedPhotoUsedId(e);
sendUserLikePost();
};
const sendUserLikePost = () => {
const url = "http://localhost:8005/api/post-user-like";
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
let data = {
like: like,
UserID: likedPhotoUserId,
};
console.log(data);
axios
.post(url, data, { headers })
.then((resp) => {
console.log(resp.data);
})
.catch((error) => {
console.log(error);
});
};
const displayUploadsData = () => {
return uploadsData.map((photos, index) => {
return (
<ImagesGrid
src={photos.url}
likes={photos.likes}
userName={photos.name}
key={index}
doubleClick={handleLikesBasedOnUserId}
value={photos.UserID}
/>
);
});
};
return <>{displayUploadsData()}</>;
};
const ImagesGrid = (props) => {
const createUserPhotoNodes = () => {
return (
<section className="gallery">
<div className="container">
<form method="POST" name="likes">
<div className="img-container">
<img
src={props.src}
alt="Photo"
className="gallery-img"
onDoubleClick={() => props.doubleClick(props.value)}
/>
<h2 className="userName">{props.userName}</h2>
<h2 className="likes" onChange={props.onChange}>
Likes {props.likes}
</h2>
</div>
</form>
</div>
</section>
);
};
return <>{createUserPhotoNodes()}</>;
};
You have hit the nail on the head – it's because setState is asynchronous and you're seeing stale props or state to boot.
With
const handleLikesBasedOnUserId = (e) => {
setLikedPhotoUsedId(e);
sendUserLikePost();
};
const sendUserLikePost = () => {
// ...
let data = {
'like': like,
'UserID': likedPhotoUserId
};
// ...
likedPhotoUserId will not have updated before you call sendUserLikePost, so likedPhotoUserId is ''. (By the time you call handleLikes... again it will have the "previous" value.)
I don't see a reason why likedPhotoUserId should be a state atom anyway, since all you use it for is sendUserLikePost – just make it a regular ol' parameter:
const handleLikesBasedOnUserId = (e) => {
sendUserLikePost(e);
};
const sendUserLikePost = (likedPhotoUserId) => {
// ...
let data = {
'like': like,
'UserID': likedPhotoUserId
};
// ...
I'm making a full stack Rick and Morty application. Characters on the screen and the user can login and click on them to add them to favorites and then click on them on the favorites page to delete them from the favorites page.
The application works but crashes after a few minutes saying that a fetch request didn't work. In network section of the developer tools, these requests to add or delete characters are coming up as (pending) and then coming up as failures like two minutes later. At the same time, the requests are working from the perspective of the application, meaning that if I add or delete characters as a user and then logout and log back in, the changes are still there. The register and login requests to the backend are working normally with statuses of 200 as well. What's happening here?
The backend:
const express = require('express');
const application = express();
const mongoose = require('mongoose');
application.use(express.json());
mongoose.connect('process.env.DATABASE_PASSWORD')
.then(console.log('Connected to database'));
const db = mongoose.connection;
const port = process.env.PORT || 8080;
application.post('/register', (request, response) => {
const username = request.body.username;
const password = request.body.password;
const favorites = [];
db.collection('data').insertOne({
username,
password,
favorites,
});
});
application.post('/login', async (request, response) => {
const username = request.body.username;
const password = request.body.password;
const findUser = await db.collection('data').findOne({
username,
password,
});
if (findUser) {
response.send({ message: 'Welcome, ' + username + "!", user: username, favorites: findUser.favorites });
} else {
response.send({ message: 'Login unsuccessful'});
}
});
application.post('/addFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.favoritesCopy;
console.log({userNow, favoritesHere});
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
)
});
application.post('/deleteFavorite', (request, response) => {
const userNow = request.body.username;
const favoritesHere = request.body.theData;
db.collection('data').updateOne(
{ username: userNow },
{ $set: { favorites: favoritesHere }},
);
});
application.listen(port, () => {
console.log('Application listening');
});
The frontend fetch add request (the delete request is similar):
import React, { useState, useEffect } from 'react';
import logo from '../rickandmortylogo.png';
import { useSelector, useDispatch } from 'react-redux';
import { addFavorite } from '../index.js';
const Body = () => {
const [characters, setCharacters] = useState([]);
const [currentName, setCurrentName] = useState('Placeholder');
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
const favorites = useSelector(state => state.favoritesList);
const userNow = useSelector(state => state.currentUser);
const loggedIn = useSelector(state => state.loggedIn);
const dispatch = useDispatch();
useEffect(() => {
let isMounted = true;
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const getData = async () => {
for (let i = 1; i < 4; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
if (isMounted) {
setCharacters(array);
setLoading(false);
}}
getData();
return () => {
isMounted = false;
}
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
}
const addData = (a, b, c, d) => {
const array = [a, b, c, d];
const favoritesCopy = [...favorites];
favoritesCopy.push(array);
dispatch(addFavorite(array));
if (loggedIn === true) {
fetch('/addFavorite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
favoritesCopy,
username: userNow,
}),
});
}
};
return (
<div className="pt-5">
<div className="text-center mt-5">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square" onClick={() => addData(item.name, item.image, item.location.name, item.status)}>
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
</div>
);
};
export default Body;
You never end the request. You don't send anything in the response and don't call response.end either, nor next. That's why your request never ends.
Here are some examples:
Success message with content
res.status(200).json({ success: true});
Success message without content
res.sendStatus(204);
Of course requests are pending, you never send anything on related actions:
Use res.send and send something, or at least in case of success, send a success status like:
204 for a no content success operation, like a DELETE for example.
201 for a POST operation creating a new resource.
5xx for errors
I have multiple API calls with fairly lengthy, yet similar, response/error handling for each call.
What is the best non-repetitive ways to make multiple independent api calls that update state using fetch?
Copying and pasting 40+ instances of fetch doesn't seem right.
I want to avoid doing this ....
fetch(url,options)
.then((response) => {
// ...
return response.json
})
.then((data) => {
setState(data)
//...
})
.catch((err) => {
//Error logic here
})
Here's what I've done so far:
I made (found and modified) a useFetch hook...
useFetch.ts
//Only calls fetch() when .load() is called.
const useFetch = (path : string, HttpMethod : string, dependencies : any = [] , body : {} | undefined = undefined) => {
const history = useHistory()
const [response, setResponse] = useState<any>({});
const [error, setError] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [controller, setController] = useState(2)
const [isReady, setIsReady] = useState<any>(false)
const load = ():void => {
setError("")
//This prevents useEffect from triggering on declaration.
if (isReady) {
//Math.random() is just to get useEffect to trigger.
setController(Math.random())
}
}
const token = localStorage.getItem("token");
let requestOptions:any = {
method: HttpMethod,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "* always",
Authorization: "Token " + token,
},
};
if (body !== undefined) {
requestOptions["body"] = {
body: JSON.stringify(body)
}
}
const URI = BASE_URI + path
useEffect(() => {
const fetchData = async () => {
if (controller !== 2) {
setIsLoading(true);
try {
const res = await fetch(URI, requestOptions);
const json = await res.json();
if (json?.action == "ENFORCE_BILLING" ) {
history.push(BILLING_CREDENTIALS_PATH, { enforceBillingPopUp: true });
}
if (json?.action == "ENFORCE_SMS_CONFIRMATION") {
// Should we log user out, as well?
history.push(CONFIRMATION_CODE_PATH)
}
if (res.ok) {
setResponse(json);
setIsLoading(false)
} else {
setError(json)
setIsLoading(false)
}
} catch (err) {
setError(err);
// Error logic here...
}
}
}
};
fetchData()
setIsReady(true)
}, [controller, ...dependencies]);
return { response, setResponse ,error, isLoading, load, isReady };
};
Component.tsx
//Inside react functional component...
// Prepares to fetch data from back-end
const data1 = useFetch(PATH1, "GET");
const data2 = useFetch(PATH2, "GET");
const data3 = useFetch(PATH3, "GET");
useEffect(() => {
// Initial on load data fetch
// .load() fetches data
data1.load();
data2.load();
data3.load();
}, [activeReservations.isReady]);
// Sort data depending on sort selection
...
Is useFetch considered bad practice? What are the advantages of using Redux, instead?
Any help would be greatly appreciated. Thanks.
I am developing a shopify theme. Inside my theme. I have a div that I am looping. Inside the div, I have an image tag where I want to pass in the image from my JSON file
where the div id == to the image id
In my HTML file, I have this.
{% for order in customer.orders %}
{% for line_item in order.line_items %}
<span id="lineNumber">{{ line_item.product_id }}</span>
<div class="order-body">
<div class="row order-single">
<div class="col-12 col-md-3">
<img src="" alt="logo" class="fit-image-product" id="imglink">
</div>
...
{% endfor %}
{% else %}
<div class="container">
<div class="lg-header">
<h3>You are not Logged In</h3>
</div>
</div>
{% endif %}
In my script file, I have this
const lineNumberElements = document.querySelectorAll("#lineNumber");
let arrayOfLineNumbers = [];
for (const key in lineNumberElements) {
if (lineNumberElements.hasOwnProperty(key)) {
const element = lineNumberElements[key];
arrayOfLineNumbers.push(element.innerHTML);
}
console.log(arrayOfLineNumbers[key]);
}
const imgsrcElements = document.querySelectorAll("#imglink");
let arrayOfimgSrc = [];
for (const key in imgsrcElements) {
if (imgsrcElements.hasOwnProperty(key)) {
const element = imgsrcElements[key];
arrayOfimgSrc.push(element.innerHTML);
}
console.log(arrayOfimgSrc[key]);
}
const geturl = `https://website.myshopify.com//admin/api/2020-04/products.json`;
fetch(geturl, {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Shopify-Access-Token": "shppa_###################",
},
})
.then((response) => {
// console.log('resolved', response);
return response.json();
})
.then((data) => {
let allproducts = data.products;
console.log(allproducts);
allproducts.forEach(function (product) {
let product_id = product.id;
let product_image = product.image.src;
//console.log(product_id);
if (product_id == arrayOfLineNumbers[key]) {
console.log("how far", product_id, product_image);
arrayOfimgSrc[key] = product_image;
}
});
})
.catch((error) => {
console.error("There has been a problem with your operation:", error);
});
I kept getting the error
There has been a problem with your operation: ReferenceError: key is not defined
UPDATE
I have been able to minimize my code and error has been removed, but the first image was sent and looped over.
const lineNumberElements = document.querySelectorAll('#lineNumber');
const imgsrcElements = document.querySelectorAll('#imglink');
const geturl = `https://beymossweb.myshopify.com//admin/api/2020-04/products.json`
fetch(geturl, {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Shopify-Access-Token": "shppa_#############",
}
})
.then((response) => {
return response.json();
})
.then(data => {
let allproducts = data.products
console.log(allproducts)
allproducts.forEach(function (product) {
let product_id = product.id
let product_image = product.image.src
console.log(product_id, product_image);
for (let j = 0; j <= imgsrcElements.length; j++) {
imgsrcElements[j].src = product_image;
}
});
})
.catch(error => {
console.error('There has been a problem with your operation:', error);
});
In order to collaborate, it is not a solution to the case you describe but I think it can put us on track to solve it
The methods commented as helpers obtain the text of each lineNumber and the value of src of each imglink, manipulating them as an array, thus solving what I commented regarding for-in
Within the successful fetch response I am not clear what you are trying, I understand that in this line product_id == arrayOfLineNumbers [key] what you want is to check if there is a value equal to arrayOfLineNumbers in the array if that is the case this line will achieve your goal arrayOfLineNumbers.includes(product_id)
Finally in this line arrayOfimgSrc[key] = product_image; your intention is not clear
key does not exist in this context
the line tries to assign product_image to an array if it is the case you must use push method
As I mentioned at the beginning this is not intended to be a solution but perhaps by correcting some things and commenting on doubts we can reach the solution
// helpers
const toTextContent = (element) => element.textContent;
const toSrc = (element) => element.src;
const nodesToArray = (nodes) => Array.from(nodes);
const getTextContent = (nodes) => nodesToArray(nodes).map(toTextContent);
const getSrc = (nodes) => nodesToArray(nodes).map(toSrc);
// collections
const arrayOfLineNumbers = getTextContent(
document.querySelectorAll("#lineNumber")
);
const arrayOfimgSrc = getSrc(document.querySelectorAll("#imglink"));
// ajax
const geturl = `https://website.myshopify.com//admin/api/2020-04/products.json`;
fetch(geturl, {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Shopify-Access-Token": "shppa_###################",
},
})
.then((response) => response.json())
.then((data) => {
data.products.forEach((product) => {
let product_id = product.id;
let product_image = product.image.src;
if (arrayOfLineNumbers.includes(product_id)) {
console.log("how far", product_id, product_image);
arrayOfimgSrc[key] = product_image;
}
});
})
.catch((error) => {
console.error("There has been a problem with your operation:", error);
});