My goal is to get the autoPagination function to run when this.props.userSaves initially updates in state. In my program it starts out as an empty array, and on initialization 100 objects are stored in the array. The problem is that autoPagination is running before the objects get stored, and thus the while loop isn't running. I've fixed this using setTimeout but I don't really see that as a long-term solution. Any ideas?
The below code is nested in a class based component.
autoPagination = async token => {
while (this.props.userSaves.length > 0) {
const { userSaves } = this.props
const lastPage = userSaves[userSaves.length-1].data.name
const userSavesObject = await axios.get (`https://oauth.reddit.com/user/${this.props.username}/saved/.json?limit=100&after=${lastPage}`, {
headers: { 'Authorization': `bearer ${token}` }
})
const currentPageSaves = userSavesObject.data.data.children
this.props.storeUserHistory(currentPageSaves)
this.props.appendUserHistory(currentPageSaves)
}
}
Full component (since requested):
import axios from 'axios';
import React from 'react';
import { connect } from 'react-redux';
import { storeUserHistory, appendUserHistory, storeInitialData } from '../actions/index.js'
class ListSaved extends React.Component {
componentDidMount (props) {
const params = new URLSearchParams(this.props.location.hash);
const token = params.get('#access_token')
this.props.storeInitialData(token)
setTimeout(() => {
this.autoPagination(token);
}, 3000)
}
autoPagination = async token => {
while (this.props.userSaves.length > 0) {
const { userSaves } = this.props
const lastPage = userSaves[userSaves.length-1].data.name
const userSavesObject = await axios.get (`https://oauth.reddit.com/user/${this.props.username}/saved/.json?limit=100&after=${lastPage}`, {
headers: { 'Authorization': `bearer ${token}` }
})
const currentPageSaves = userSavesObject.data.data.children
this.props.storeUserHistory(currentPageSaves)
this.props.appendUserHistory(currentPageSaves)
}
}
renderPostTitles = () => {
return this.props.totalSaves.map((saved) => {
return (
<div key={saved.data.id}>
<div>{saved.data.title}</div>
</div>
)
})
}
render () {
return <div>{this.renderPostTitles()}</div>
}
}
const mapStateToProps = state => {
console.log(state)
return {
username: state.username,
userSaves: state.userHistory,
totalSaves: state.totalUserHistory
}
}
export default connect(mapStateToProps, { storeUserHistory, appendUserHistory, storeInitialData })(ListSaved);
Take a variable and set it true initially.. Run the function when you get data in your props and make the variable false so that it don't run again..
constructor (props)
{
super(props)
this.myvar = true
}
componentWillRecieveProps(nextProps)
{
if(this.myvar)
{
if(check if get your data)
{
// run your function
this.myvar= false
}
}
}
Corrected Component. Every-time the component updates the function is run. Component is updated a first time right after mounting
import axios from 'axios';
import React from 'react';
import { connect } from 'react-redux';
import { storeUserHistory, appendUserHistory, storeInitialData } from '../actions/index.js'
class ListSaved extends React.Component {
componentDidMount (props) {
const params = new URLSearchParams(this.props.location.hash);
const token = params.get('#access_token')
this.props.storeInitialData(token)
}
componentDidUpdate (props) {
this.autoPagination(token);
}
autoPagination = async token => {
while (this.props.userSaves.length > 0) {
const { userSaves } = this.props
const lastPage = userSaves[userSaves.length-1].data.name
const userSavesObject = await axios.get (`https://oauth.reddit.com/user/${this.props.username}/saved/.json?limit=100&after=${lastPage}`, {
headers: { 'Authorization': `bearer ${token}` }
})
const currentPageSaves = userSavesObject.data.data.children
this.props.storeUserHistory(currentPageSaves)
this.props.appendUserHistory(currentPageSaves)
}
}
renderPostTitles = () => {
return this.props.totalSaves.map((saved) => {
return (
<div key={saved.data.id}>
<div>{saved.data.title}</div>
</div>
)
})
}
render () {
return <div>{this.renderPostTitles()}</div>
}
}
const mapStateToProps = state => {
console.log(state)
return {
username: state.username,
userSaves: state.userHistory,
totalSaves: state.totalUserHistory
}
}
export default connect(mapStateToProps, { storeUserHistory, appendUserHistory, storeInitialData })(ListSaved);
Related
I have a created useAxiosPrivate hook and I want to use it in a service function I have created using axios which I used to export diffrent methods. But since its not a functional or class component I get an error react hooks must be called in a react function component or a custom react hook function
useAxiosPrivate.tsx
import { axiosPrivate } from '../api/axios'
import { useEffect } from 'react'
import useRefreshToken from './useRefreshToken'
import useAuth from './useAuth'
const useAxiosPrivate = () => {
const refresh = useRefreshToken()
const { auth }: any = useAuth()
useEffect(() => {
const requestIntercept = axiosPrivate.interceptors.request.use(
(config) => {
config.headers = config.headers ?? {}
if (!config.headers['Authorization']) {
config.headers['Authorization'] = `Bearer ${auth?.accessToken}`
}
return config
},
(error) => Promise.reject(error),
)
const responseIntercept = axiosPrivate.interceptors.response.use(
(response) => response,
async (error) => {
const prevRequest = error?.config
if (
(error?.response?.status === 403 || error?.response?.status === 401) &&
!prevRequest?.sent
) {
prevRequest.sent = true
const newAccessToken = await refresh()
prevRequest.headers['Authorization'] = `Bearer ${newAccessToken}`
return axiosPrivate(prevRequest)
}
return Promise.reject(error)
},
)
return () => {
axiosPrivate.interceptors.request.eject(requestIntercept)
axiosPrivate.interceptors.response.eject(responseIntercept)
}
}, [auth, refresh])
return axiosPrivate
}
export default useAxiosPrivate
I want to use this in auth.service.tsx
import useAxiosPrivate from "../hooks/useAxiosPrivate"
const axiosPrivate = useAxiosPrivate(); <-- 'I want to use this in this'
export const SharedService {
UpdateProfile: async (firstName:string, lastName:string) => {
const response = await axiosPrivate.put('/user/me',{
firstName,
lastName,
})
}
I get error that hooks should be used at top level or inside functional component or class how do I fix it ?
Your service must be a hook as well so it can use other hooks
import useAxiosPrivate from "../hooks/useAxiosPrivate";
export const useSharedService = () => {
const axiosPrivate = useAxiosPrivate();
return {
UpdateProfile: async (firstName: string, lastName: string) => {
const response = await axiosPrivate.put("/user/me", {
firstName,
lastName,
});
},
};
};
I'm working on my first app. I put response from the server in an empty array in the state, but when I try to work with this array it throws undefined. On the first render everything seems OK but any futher manipulations fail. Please, help. I'm desperate here. My code:
App component:
import { React, useEffect, useContext } from "react";
import { getExchangeCourse } from "./api/currencyApi";
import { ADD_CURRENCY } from "./reducer/currencyReducer";
import { CurrenciesConverterContextComponent } from "./context/Context";
import Header from "./components/Header";
function App() {
const [, dispatch] = useContext(CurrenciesConverterContextComponent);
useEffect(() => {
getExchangeCourse().then(data => dispatch({
type: ADD_CURRENCY,
payload: data
}))
}, [])
return (
<div className="App">
<Header />
</div>
);
}
Header component:
export default function Header() {
const [{currencies}] = useContext(CurrenciesConverterContextComponent);
let USD = currencies.find(e => e.cc === "USD");
let EUR = currencies.find(e => e.cc === "EUR");
let GBP = currencies.find(e => e.cc === "GBP");
**these functions above work on a first render and then fail**
return (
<div className='converter-header'>
<span>{USD.cc} : {USD.rate}</span>
<span>{EUR.cc} : {EUR.rate}</span>
<span>{GBP.cc} : {GBP.rate}</span>
</div>
)
}
Reducer:
export const initialState = {
currencies: [],
}
export const ADD_CURRENCY = '[CURRENCIES] Add Currency';
export const currenciesReducer = ( state = initialState, action) => {
switch(action.type) {
case ADD_CURRENCY:
return {
...state,
// currencies: [...state.currencies, action.payload]
currencies: action.payload
};
default: {
return {
...state
}
}
}
}
API request:
import axios from 'axios';
const currencyAPI = axios.create({
baseURL: 'https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?json',
headers: {"Content-type": "application/json"},
})
export async function getExchangeCourse() {
const {data} = await currencyAPI.get()
console.log(data);
return data;
}
I am implementing a simple logout functionality if my local storage doesn't have a particular key-value pair and if the value is empty or if the 'token' inside the value is expired.
My current Code: TokenExpired.js
import { isExpired } from "react-jwt";
import { useNavigate } from "react-router-dom";
export const VerifyAccessToken = () => {
const navigate = useNavigate()
const Data = localStorage.getItem('Admin Credentials')
const existanceOfData = Data !== null
if (existanceOfData) {
if (Data && Data !== 'undefined') {
const tokenExpired = isExpired(JSON.parse(Data).accessToken);
if (tokenExpired) {
localStorage.removeItem("Admin Credentials");
navigate('/')
}
} else {
localStorage.removeItem("Admin Credentials");
navigate('/')
}
} else {
navigate('/')
}
}
I am using this in My Dashboard Page : Dashboard/Dashboard/js
import "./Dashboard.scss";
import { adminAuth } from "../../helpers/AdminInformation";
import { VerifyAccessToken } from "../../helpers/TokenExpired";
// components ---------------------------------
certain components
import { useEffect, useState } from "react";
const Dashboard = () => {
const [dashboard, setDashboard] = useState({ received: 0, expected: 0 })
const token = adminAuth.accessToken;
useEffect(() => {
fetch(baseURL + 'api/dashboard/', {
headers: {
token: `Bearer ${token}`
}
}).then(res => res.json()).then(json => setDashboard(json));
}, [token])
VerifyAccessToken();
return (
<div className="dashboard">
content
</div>
);
}
export default Dashboard;
Whenever I try to delete that key value after logging in, it shows error:
I Found the Answer to my Question:
I figured I need to make my Routes Strong so that there are no warning about my routes in the console
I Created Layout Component for my Dashboard Page & Update Token Expired Code with useEffect(), It worked...
My Updated Code: TokenExpired.js
import { isExpired } from "react-jwt";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
export const VerifyAccessToken = () => {
const navigate = useNavigate()
const Data = localStorage.getItem('Admin Credentials')
const existanceOfData = Data !== null
useEffect(() => {
if (existanceOfData) {
if (Data && Data !== 'undefined') {
const tokenExpired = isExpired(JSON.parse(Data).accessToken);
if (tokenExpired) {
navigate('/')
}
} else {
navigate('/')
}
} else {
navigate('/')
}
}, [Data, existanceOfData, navigate]);
}
import React from "react";
import { UserContext } from "./../contexts";
import {
removeStoredAuthData,
storedAuthIsValid,
storeNewAuthData,
} from "./../utils/auth";
import { getUserInfos } from "./../api/userAuthentication";
class UserProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
user: "",
};
}
render() {
return (
<UserContext.Provider
value={{
user: this.state.user,
clearUserProfile: () => {
const user = "";
removeStoredAuthData();
this.setState({ user });
},
saveUserProfile: (response) => {
const user = response.data;
storeNewAuthData(response);
this.setState({ user });
},
populateUserProfile: (displayLoader, hideLoader) => {
const storedToken = localStorage.getItem("appsante-token");
const storedId = localStorage.getItem("appsante-id");
if (storedAuthIsValid()) {
displayLoader(() => {
getUserInfos(storedId)
.then((response) => {
const user = { ...response.data, token: storedToken };
this.setState({ user }, hideLoader());
})
.catch((error) => console.log(error));
});
}
},
}}
>
{this.props.children}
</UserContext.Provider>
);
}
}
export default UserProvider;
Hi everyone !
I trying to convert a React class component into a function component, with hooks.
But I can't find a way to deal properly with that line :
this.setState({ user }, hideLoader());
Unlike setState in class components, useState doesn't take a callback as second parameter, and I can't find how to achieve it with useEffect.
Could anyone help me ? Thanks !
Because the loader's presence can't be determined from the value in / change in user alone, you'll need another state variable, maybe one that contains the callback - perhaps call it hideLoader. After getUserInfos resolves, call setHideLoader with the callback, so that a useEffect hook with that function as a dependency can see the change and call the callback:
const [hideLoader, setHideLoader] = useState();
useEffect(() => {
if (hideLoader) {
hideLoader(); // or, if this is a HOF: hideLoader()()
setHideLoader(); // callback done; remove callback from state
}
}, [hideLoader]);
// ...
populateUserProfile: (displayLoader, hideLoaderParam) => {
// ...
getUserInfos(storedId)
.then((response) => {
setUser({ ...response.data, token: storedToken }); // hook version
setHideLoader(hideLoaderParam);
})
and the rest of your code can be mostly the same - only call setHideLoader up above, inside getUserInfos.
I think you should do this :-
import React, { useState } from 'react';
const [user, setUser] = useState("");
populateUserProfile: async (displayLoader, hideLoader) => {
const storedToken = localStorage.getItem("appsante-token");
const storedId = localStorage.getItem("appsante-id");
if (storedAuthIsValid()) {
displayLoader();
let response = await getUserInfos(storedId)
const user = { ...response.data, token: storedToken };
setUser(user);
hideLoader();
};
}
I have created function that was to return JSX object, but when I call it in render method it doesn't display anything.
import React, { Component } from 'react'
import './styles/Mainbody.css'
import axios from 'axios';
import { FORTNITE_IO } from '../config'
class Mainbody extends Component {
getTopPlayersInfo = (player) => {
axios.get(`https://fortniteapi.io/lookup?username=${player}`, {
headers: {
Authorization: FORTNITE_IO
}
})
.then(res => {
const user_id = res.data.account_id
axios.get(`https://fortniteapi.io/stats?account=${user_id}`, {
headers: {
Authorization: FORTNITE_IO
}
})
.then(res => {
const kills = res.data.global_stats.duo.kills + res.data.global_stats.solo.kills + res.data.global_stats.squad.kills
const solo = res.data.global_stats.solo.placetop1
const duo = res.data.global_stats.duo.placetop1
const squad = res.data.global_stats.squad.placetop1
return (
<React.Fragment>
<p className="stats">Kills: {kills}</p>
<p className="stats">Solo Wins: {solo}</p>
<p className="stats">duos Wins: {duo}</p>
<p className="stats">Squad Wins: {squad}</p>
</React.Fragment>
)
})
.catch(err => err)
})
.catch(err => err)
}
render() {
return (
<React.Fragment>
{this.getTopPlayersInfo('jerugba')}
</React.Frangment>
export default Mainbody;
Don't worry, patch this is very simple ^^
First create a constructor props with super props. Inside create a state by this.state : {}.
When you fetch your informations, send these information on the state by setState();
Now, on your final render(), you can select what you want to render on a div by this.state.kills (For example)
Have fun ^^
initialise state and when component mounts update state with latest global_stats and it will render with latest values , modified your code as follows
import React, { Component } from 'react'
import './styles/Mainbody.css'
import axios from 'axios';
import { FORTNITE_IO } from '../config'
class Mainbody extends Component {
constructor(props) {
super(props);
this.state = {
kills: 0,
solo: 0,
duo: 0,
squad: 0,
};
}
componentDidMount() {
this.getTopPlayersInfo('jerugba');
}
getTopPlayersInfo = (player) => {
axios.get(`https://fortniteapi.io/lookup?username=${player}`, {
headers: {
Authorization: FORTNITE_IO
}
})
.then(res => {
const user_id = res.data.account_id
axios.get(`https://fortniteapi.io/stats?account=${user_id}`, {
headers: {
Authorization: FORTNITE_IO
}
})
.then(res => {
const kills = res.data.global_stats.duo.kills + res.data.global_stats.solo.kills + res.data.global_stats.squad.kills;
const solo = res.data.global_stats.solo.placetop1;
const duo = res.data.global_stats.duo.placetop1;
const squad = res.data.global_stats.squad.placetop1;
// set state here
this.setState({
kills,
solo,
duo,
squad,
});
})
.catch(err => err)
})
.catch(err => err)
}
render() {
const { kills, solo, duo, squad } = this.state;
return (
<React.Fragment><p className="stats">Kills: {kills}</p>
<p className="stats">Solo Wins: {solo}</p>
<p className="stats">duos Wins: {duo}</p>
<p className="stats">Squad Wins: {squad}</p>
<React.Fragment>
);
}
}
export default Mainbody;
also try async await if you want something like this
getTopPlayersInfo = async(player) => {
try {
const headers = {
Authorization: FORTNITE_IO
};
const userData = await axios.get(`https://fortniteapi.io/lookup?username=${player}`, { headers });
const { account_id } = userData;
const stats = await axios.get(`https://fortniteapi.io/stats?account=${account_id}`, { headers });
// and process stats and set you state here
} catch (e) {
// handle your errors
}
}
You can also use react hooks (useEffect & useState)