REACT - POST API route on likes and dislikes - reactjs

I'm having some trouble figuring out the answer of the following issue, if any of you could help, that would be great :
I've set up my routes and the API works fine. One of those routes consists of liking and disliking posts, the method for those likes and dislikes being POST.
My console does not show any error and even responds to logs inside the function.
My problems are the following:
First I'm wondering if I can retrieve previously made likes or dislikes without having to create a get route. (if that makes sense, sorry I'm fairly new.)
Secondly, well, managing to POST those likes and dislikes.
Here's the code.
Frontend.
const currentId = localStorage.getItem('userId');
const params = useParams();
const [post, setPost] = useState({
title: '',
description: '',
image: null,
likes: '',
dislikes: '',
usersLiked: [],
usersDisliked: [],
});
useEffect(() => {
axios
.get('http://localhost:5000/api/posts/' + params.id, config)
.then((response) => {
setPost(response.data);
});
}, []);
const [likeState, setLikeState] = useState(0);
const usersLiked = post.usersLiked;
const usersDisliked = post.usersDisliked;
function likef() {
if (likeState === 0 && usersLiked.userId != currentId) {
setLikeState(1);
usersLiked.push(currentId);
post.likes = post.likes + 1;
saveLikes(post.likes);
} else if (likeState === 1) {
setLikeState(0);
usersLiked.shift();
post.likes = post.likes - 1;
}
}
function dislikef() {
if (likeState === 0 && usersDisliked.userId != currentId) {
setLikeState(-1);
usersDisliked.push(currentId);
post.dislikes = post.dislikes + 1;
saveLikes(post.dislikes);
} else if (likeState === -1) {
setLikeState(0);
usersDisliked.shift();
post.dislikes = post.dislikes - 1;
saveLikes(post.dislikes);
}
}
const saveLikes = () => {
const id = post._id;
const url = `http://localhost:5000/api/posts/${id}/like`;
fetch(url, {
method: 'POST',
headers: { Authorization: `Bearer ${AbstractManager.token}` },
})
.then(() => {
setLikeState(response.data);
})
.catch((e) => {});
};
Template code
<div className="like">
<button
id="btn-pink"
onClick={likef}
className={[
likeState === 1 ? 'btn-pink' : null,
'btn-pink',
].join()}
>
<FontAwesomeIcon icon={faHeart} /> {post.likes}
</button>
<button
id="btn-red"
onClick={dislikef}
className={[
likeState === -1 ? 'btn-red' : null,
'btn-red',
].join()}
>
<FontAwesomeIcon icon={faHeartBroken} /> {post.dislikes}
</button>
If any one of you could help, that would be awesome. Thank you in advance.

Related

web3 staking function not displaying staked history in reactjs/solidity

I created a simple dapp with react/truffle/web3 that allows users to enter a number of tokens and submit it to stake it, the problem that I got is that when clicked on submit button, Metamask popup to confirm the transaction(approuve) but I don't get the second popup to confirm stakeTokens function, hence, no transaction history displayed.
Dapp link: https://doxa-staking.netlify.app.
The stake smart contract code deployed on rinkeby: https://rinkeby.etherscan.io/address/0xAD015a006755b389d8e5BC2680cc5081dc1d0abd#code
The reactjs web3 code in github:
https://github.com/isofttechn/staking-dapp/blob/main/src/components/Home/index.js
My Stake Approval Function
approval = async () => {
const { web3, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei("99999999", "ether");
const stakingContractAddress = process.env.REACT_APP_DOXACONTRACT_ADDRESS;
const approval = await token.methods
.approve(stakingContractAddress, tokenAmount)
.send({ from: address });
console.log(approval);
this.setState({ isApproved: true });
await this.checkAllowance();
};
My Stake Function:
stakeAZO = async () => {
this.setState({ stakeloading: true });
let type;
if (this.state.stakeType) {
type = 1;
} else type = 0;
if (this.state.stakeValue > 0 && this.state.yearsValue > 0) {
const { web3, staking, token, address } = this.props.wallet;
const tokenAmount = web3.utils.toWei(
this.state.stakeValue.toString(),
"ether"
);
var time = Date.now();
console.log("address", [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
]);
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
userAddress: address,
contractAddress: process.env.REACT_APP_DOXACONTRACT_ADDRESS,
amount: tokenAmount,
id: type,
noOfDays: this.state.yearsValue,
timestamp: time,
}),
};
const response1 = await fetch(
process.env.REACT_APP_API_URL + "/getsignature",
requestOptions
);
const data1 = await response1.json();
var signaturelkn = data1.result;
var sigtuplelkn = [
address,
process.env.REACT_APP_DOXACONTRACT_ADDRESS,
tokenAmount,
type,
this.state.yearsValue,
time,
signaturelkn,
];
try {
const stake = await staking.methods
.stake(tokenAmount, this.state.yearsValue, sigtuplelkn)
.send({ from: address });
this.setState({ stakeloading: false });
} catch (err) {
console.log(err);
this.setState({ stakeloading: false });
}
// console.log(stake);
this.getStakeRecords();
if (this.state.stakeType) {
this.getTokenBalance();
this.setState({ stakeloading: false });
}
} else {
this.setState({ stakeloading: false });
alert("Amount of AZO or days should be more than 0!");
}
};
How I rendered it:
<div className="d-block d-md-flex bottom-btn-cont">
{!this.state.isApproved && (
<button
className="btn-btn stake-btn"
onClick={() =>
this.props.wallet.connected
? this.approval()
: alert("Connect to wallet!")
}
>
Approve AZO
</button>
)}
<button
className="btn-btn stake-btn"
style={{
backgroundColor: this.state.isApproved
? "#002365"
: "#e2e2e2",
}}
disabled={!this.state.isApproved}
onClick={() =>
this.state.isApproved
? this.stakeAZO()
: alert("Approve tokens before staking!")
}
>
STAKE AZO{" "}
{this.state.stakeloading ? (
<img
style={{ width: "20px", height: "20px" }}
src={buyLoader}
></img>
) : null}
</button>
</div>

prevent api from being called before state is updated

I have a list of objects. I want to make an api call once the location field of the object is changed. So for that, I have a useEffect that has id, index and location as its dependencies. I have set a null check for the location, if the location isn't empty, I want to make the api call. But the thing is, the api is being called even when the location is empty, and I end up getting a 400. How can I fix this and make the call once location isn't empty?
const [plants, setPlants] = useState([
{
name: 'Plant 1',
id: uuidv4(),
location: '',
coords: {},
country: '',
isOffshore: false,
}
]);
const [locationIDObject, setlocationIDObject] = useState({
id: plants[0].id,
index: 0
});
const handlePlantLocChange = (id, index, value) => {
setPlants(
plants.map(plant =>
plant.id === id
? {...plant, location: value}
: plant
)
)
setlocationIDObject({
id: id,
index: index
})
}
const getCoords = (id, index) => {
axios.get('http://localhost:3002/api/coords', {
params: {
locAddress: plants[index].location
}
}).then((response) => {
if(response.status === 200) {
handlePlantInfoChange(id, PlantInfo.COORD, response.data)
}
})
}
const handler = useCallback(debounce(getCoords, 5000), []);
useDeepCompareEffect(() => {
if(plants[locationIDObject.index].location !== '')
handler(locationIDObject.id, locationIDObject.index);
}, [ locationIDObject, plants[locationIDObject.index].location])
return (
<div className='plant-inps-wrapper'>
{
plants.map((item, idx) => {
return (
<div key={item.id} className="org-input-wrapper">
<input placeholder={`${item.isOffshore ? 'Offshore' : 'Plant ' + (idx+1) + ' location'}`} onChange={(e) => handlePlantLocChange(item.id, idx, e.target.value)} value={item.isOffshore ? 'Offshore' : item.location} className="org-input smaller-input"/>
</div>
)
})
}
</div>
)
I think your useCallback is not updating when value of your variables is changing and that is the issue:
Although the check is correct, but the call is made for older values of the variable. You should update the dependencies of your useCallback:
console.log(plants) inside getCoords might help you investigate.
Try this:
const handler = useCallback(debounce(getCoords, 5000), [plants]);
So it turns out the issue was with my debouncing function. I don't know what exactly the issue was, but everything worked as expected when I replaced the debouncing function with this:
useEffect(() => {
console.log("it changing")
const delayDebounceFn = setTimeout(() => {
getCoords(locationIDObject.id, locationIDObject.index)
}, 4000)
return () => clearTimeout(delayDebounceFn)
},[...plants.map(item => item.location), locationIDObject.id, locationIDObject.index])

Problem with network timeout on PayPal SDK integration react-paypal-button-v2

Hello I am having an error implementing PayPal for my practice website. The error is shown below. Code otherwise works unless I click on smart button then leave it sitting. How can I error handle this? I am using react-paypal-button-v2. Button code is also shown here.
STATE CODE
function OrderScreen() {
...
const addPayPalScript = () => {
const script = document.createElement('script')
script.type = 'text/javascript'
// const ClIENT_ID = process.env.REACT_APP_PAYPAL_CLIENT_ID
script.src = `https://www.paypal.com/sdk/js?client-id=${CLIENT_ID}`
script.async = true
script.onload = () => {
setSdkReady(true)
}
document.body.appendChild(script)
}
useEffect(() => {
if(!order || successPay || order._id !== Number(orderId)) {
dispatch({type: ORDER_PAY_RESET})
dispatch(getOrderDetails(orderId))
} else if (!order.isPaid) {
if(!window.paypal) {
addPayPalScript()
} else {
setSdkReady(true)
}
}
if(error?.includes('token not valid')) {
dispatch(logout())
dispatch('login')
}
}, [dispatch, order, orderId, error, successPay])
const successPaymentHandler = (paymentResult) => {
dispatch(payOrder(orderId, paymentResult))
}
BUTTON CODE
{!sdkReady ? (
<Loader />
) : (
<PayPalButton
createOrder={(data, actions) => {
return actions.order.create({
purchase_units: [{
amount: {
currency_code: "USD",
value: order.totalPrice
},
...
}
}],
application_context: {
locale: 'us-US',
shipping_preference: 'SET_PROVIDED_ADDRESS',
}
});
}}
onSuccess={successPaymentHandler}
onError={(err)=>setErrorPayPal(err)}
/>
)}
</List
Most likely that 'stats' request is being blocked by some adblock or browser anti-tracking mechanism. Regardless of the specifics it's not a problem, ignore it completely.
Separately, react-paypal-button-v2 is not an official package. Consider using react-paypal-js instead.

Create a HTML ordered list on basis of JSON response in React

I want to create a ordered html list with the JSON response values that I recieve by my backend. I have want to map on the result and per result generate a list item. But when I run this it's only showing 1. with nothing instead of:
Started flow version: 1
Started flow version: 2
I have the following react code:
const Deployment = (deployment_proces) => {
Object.values(deployment_proces).map(p => console.log(p))
const deploymentProcesItems = Object.values(deployment_proces).map(proces => (
<li key={proces.id}>
{proces.message}
</li>
));
return (
<div className='deployment-progress-container'>
<h6 className='deployment-progress-title'>Deployment progress:</h6>
<ol className='deployment-progress-list'>
{ deploymentProcesItems }
</ol>
</div>
)
}
My JSON response is as followes:
[
{
"code": 200,
"id": 2251799813688456,
"message": "Started flow version: 1",
"name": ""
},
{
"code": 200,
"id": 2251799813688479,
"message": "Started flow version: 1",
"name": ""
}
]
The deployment_proces variable is of the type object with as value the above JSON response. So I found a way in this article to convert a object to an array, because a object doesn't have a map function. And when I run the Object.values(deployment_proces).map(p => console.log(p)) it returns the same JSON as above in the console with length: 2 and proto: array(). So it is a array now (right?) and next I want to map on that array using the map function that iterates and creates a <li key={id}>{message}</li> per entry.
Any ideas how to fix this /or advice how I can do it better? Because I feel this is way to complex for a simpel iteration over a list.
EDIT
My deployment_proces variable value is equal to the return value of the DeployComponent function shown below.
const DeployComponent = async (data, basicAuth) => {
const response = await ApiCall(constants.ENDPOINTS.DEPLOY_COMPONENT, basicAuth, data);
if(!response.ok) {
throw new Error('Could not create component.');
}
return await HandleCreate(response);
}
const HandleCreate = async response => {
const result = await response.json();
if (result) {
return result;
} else {
throw Error('Message invalid response body.');
}
}
And the code where I use the <Deployment /> component:
const ButtonGroup = ({ data, deployment, basicAuth, doSetDeployment }) => {
const handleDeployment = async () => {
if (!data) {
return
}
var newDeployment = await DeployComponent(data, basicAuth);
doSetDeployment(newDeployment);
}
const isDisabled = () => {
if (Array.isArray(data) && data.length > 0) {
//now the data array has blocks check if the diagram has no errors
return DiagramHasError(data)
}
if (!data) {
return true;
}
if (Array.isArray(data) && data.length === 0) {
return true;
}
return false;
}
return (
<div className='button-group' >
{deployment ? <Deployment deployment_proces={deployment} /> : null}
<Button className={`btn ${isDisabled() ? 'disabled' : ''}`} color='green' text='Deploy diagram' onClick={() => handleDeployment()} disabled={isDisabled()} />
<Button className={`btn ${isDisabled() ? 'disabled' : ''}`} text='Save' disabled={isDisabled()} />
</div>
)
}
const mapStateToProps = state => ({
data: state.drawer.data,
deployment: state.drawer.deployment,
basicAuth: state.auth.basicAuth
})
const mapDispatchToProps = {
doSetDeployment: setDeployment
}
export default connect(mapStateToProps, mapDispatchToProps)(ButtonGroup)

How to properly paginate data in React with Firestore?

As I am starting my experience with Firebase I am a little bit struggling with the pagination of posts on my blog website :(.
I think I kind of understood the docs from Google and I know how to move the pagination to the next page. However, I have absolutely no clue how to paginate back to the previous page.
Basically, I wanted to have a simple pagination component which will look something like that: < 1 2 3 [...] > (where you can paginate next and back using the arrows).
It is fine to paginate to the next page but, when it comes to paginating back I cannot find any proper tutorial to do it in pure React.
I have tried to use various methods from startAt, endAt, endBefore etc. But the result was or an error or it was moving me back to the first page (even when I was on the third or fourth)
I even tried to find the first object in an array and use it as endBefore but it resulted again in paginating back to the first page.
That's how my code looks right now (yes I know that pageNext() and pagePrev() are the same)
import React, { Component } from 'react'
import { withFirebase } from './Firebase'
import Post from './Post'
import '../scss/Post.scss'
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
loading:false,
posts:[],
post_id:[],
lastVisible:null,
limit:2
}
this.handlePageNext = this.handlePageNext.bind(this);
}
componentDidMount() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.limit(2)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible: lastVisible});
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
handlePageNext() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.startAt(this.state.lastVisible)
.limit(this.state.limit)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible:lastVisible });
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
handlePagePrev() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.startAt(this.state.lastVisible)
.limit(this.state.limit)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible:lastVisible});
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
render() {
return (
<div className='posts'>
<div className='row'>
{this.state.posts.map((post, i) => (
<Post
key={i}
title={post.title}
author={post.author}
desc={post.desc}
text={post.text}
id={this.state.post_id[i]}
date={post.date}
imgURL={post.imgURL}/>
))}
{this.state.loading && <p>Loading...</p>}
<button className='btn' onClick={() => this.handlePagePrev()}>←</button>
<button className='btn' onClick={() => this.handlePageNext()}>></button>
</div>
</div>
)
}
}
export default withFirebase(Posts);
I wanted to have a simple pagination using buttons (left and right arrows) but I am struggling with it for already 3rd hour and cannot find the proper solution to this.
You have to keep the "lastVisible" and pass it to startAfter(). 2 functions I wrote below:
export const getMostRecentPostsFirstPage = (limit, specificUserId) => {
if (!Number.isInteger(limit) || limit < 1) {
throw new Error('limit must be a positive integer');
}
const collection = Firestore.collection('posts');
let query = null;
if (specificUserId) {
query = collection
.where('userId', '==', `${specificUserId}`)
.orderBy('postedTimestamp', 'desc')
.limit(limit);
} else {
query = collection.orderBy('postedTimestamp', 'desc').limit(limit);
}
return new Promise((resolve, reject) => {
const posts = [];
query
.get()
.then(snapshot => {
const lastVisible = snapshot.docs[snapshot.docs.length - 1];
snapshot.forEach(post => {
posts.push(post.data());
});
const hasMore = posts.length == limit;
resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
})
.catch(error => reject(error));
});
};
export const getMostRecentPostsNextPage = (lastVisible, limit, specificUserId) => {
if (!lastVisible) {
throw new Error('Need to provide lastVisible argument');
}
if (!Number.isInteger(limit) || limit < 1) {
throw new Error('limit must be a positive integer');
}
const collection = Firestore.collection('posts');
let query = null;
if (specificUserId) {
query = collection
.where('userId', '==', `${specificUserId}`)
.orderBy('postedTimestamp', 'desc')
.startAfter(lastVisible)
.limit(limit);
} else {
query = collection
.orderBy('postedTimestamp', 'desc')
.startAfter(lastVisible)
.limit(limit);
}
return new Promise((resolve, reject) => {
const posts = [];
query
.get()
.then(snapshot => {
const lastVisible = snapshot.docs[snapshot.docs.length - 1];
snapshot.forEach(post => {
posts.push(post.data());
});
const hasMore = posts.length == limit;
resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
})
.catch(error => reject(error));
});
};
It uses redux-saga, but you get the idea.
on first query, do not call "startAfter()", but do on the subsequent queries, and you must save "lastVisible" between each call.
Here is standard pagination by using Firebase in reactjs.

Resources