// Want to show spinner while posting and then success/error message using react-toastify
Is it possible?
axios.post("/orders.json", order)
.then((response) => {
console.log(response.status);
})
.catch((error) => {
console.log(error);
});
import { toast } from "react-toastify";
const promise=axios.post("/orders.json", order)
const res = await toast.promise(promise, {
pending: "Posting",
success: "Posted",
error: "error message",
});
Yes it is possible. You can have a state called isLoading and you can set it to true when you submit a post request and you can check for isLoading before rendering your component like below.
if(isLoading) {
return (<Spinner />)
}
And you can use toast to show success/error message after post method is executed.
Like below
toast.error('Sorry request failed')
or
toast.success('Request successfull')
But before use toast you have wrap your App component in toast container like below.
import React from 'react';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
function App(){
const notify = () => toast("Wow so easy!");
return (
<div>
<button onClick={notify}>Notify!</button>
<ToastContainer />
</div>
);
}
This will solve your question.
import React, { useEffect, useState } from "react";
import axios from "axios";
import { toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
toast.configure({
position: toast.POSITION.BOTTOM_RIGHT
});
export default function App() {
const [state, setState] = useState({
loading: true,
dataArray: []
});
const myRequest = async () => {
try {
const request = await axios.get(`/generic_data/search_skills`);
const { error, msg, data } = request.data;
if (error) throw new Error(msg);
setState({ ...state, loading: false, dataArray: data });
toast.success("All good, we have the data");
} catch (e) {
toast.error("Upps, someting went wrong");
}
};
useEffect(()=>{
myRequest()
},[])
if (state.loading) {
return "Loading data from server.. this will take long time...";
}
return <div>The request has ended. We have the data</div>;
}
Related
i use apollo client and react in project
i want appear loading indicator while network request, so i set react variables and change it inside apollo link
but it cause cannot update component(Indicator) while rendering a diffrend component(component that call useQuery hook)
loadingLink.ts
import { ApolloLink } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
export const loadingLink = new ApolloLink((operation, forward) => {
loadingVar(true);
return forward(operation).map(data => {
loadingVar(false);
return data;
});
});
loadingIndicator.ts
import React from 'react';
import { useReactiveVar } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
import { Indicator } from './Indicator';
const LoadingIndicator: React.FC = () => {
const loading = useReactiveVar(loadingVar);
if (!loading) return null;
return <Indicator />;
};
export default LoadingIndicator;
error message
error message
BatchList.tsx
const BatchList = () => {
const {data, error} = useQuery(~~~);
if(error) return null;
if(loading || !data) return null;
return ~~~~
}
in documentation, they say should use useEffect hook but inside ApolloLink i can't use useEffect hook
How can i fix it
I have been trying to create a checkAuth function all day, backend is creating a token and saving it in mongodb, when someone logs in the token gets overwritten to a new token, so only one session can be logged in at a time. However, my checkAuth function always returns true.
CheckAuth.js
import React from "react"
import axios from "axios"
const checkAuth = async () => {
const userInfo = localStorage.getItem("userInfo");
const info = JSON.parse(userInfo);
const email = info.email;
const origintoken = info.token;
console.log("origin");
try {
const config = {
headers: {
"Content-type": "application/json"
}
}
const { data } = await axios.post (
"/api/users/token",
{email},
config
);
const token = data.token;
console.log("origin");
if (origintoken === token) {
console.log("origin");
return true;
}
else {
console.log("else");
return false;
}
} catch (error) {
console.log("error");
return false;
}
}
export default checkAuth
LandingPage.js
import React from "react"
import AuthCheck from "./CheckAuth.js"
import { useEffect } from "react"
import {Redirect} from "react-router-dom"
import { useState} from "react"
import checkAuth from "./CheckAuth.js"
export default function LandingPage() {
const [redirect, setRedirect] = useState(false);
useEffect(() => {
if(!checkAuth()) {
setRedirect(true);
console.log("false");
}}, [])
if (redirect) {
<Redirect to="/"/>
}
return (
<div>
<h1>whatsup</h1>
</div>
)
}
Serverside:
const checkToken = asyncHandler(async (req, res) => {
const email = req.body;
const user = await User.findOne(email)
if (user) {
res.json({
token: user.token,
});
} else {
res.status(400)
throw new Error("Niet ingelogd.")
}
});
As checkAuth is an async function, it always returns a promise which resolves to the value that you return in try block, or rejects with the value that you return in the catch block.
resolves to either true or false:
if (origintoken === token) {
console.log("origin");
return true;
}
else {
console.log("else");
return false;
}
rejects with false:
catch (error) {
console.log("error");
return false;
}
So you can't test its return value like a normal function. instead:
useEffect(async () => {
let authResult=await checkAuth();
if (!authResult) { // first correction
setRedirect(true);
}
}, [])
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#description
The component should probably look something like this:
import React from "react"
import AuthCheck from "./CheckAuth.js"
import { useEffect } from "react"
import { Redirect } from "react-router-dom"
import { useState } from "react"
import checkAuth from "./CheckAuth.js"
export default function LandingPage() {
const [redirect, setRedirect] = useState(false);
useEffect(() => {
if (!checkAuth()) { // first correction, call the function
setRedirect(true);
}
}, [])
if (redirect) {
return <Redirect to="/"/> // second correction, missing return
}
return (
<div>
<h1>whatsup</h1>
</div>
)
}
You can also do this in the useEffect:
useEffect(() => {
setRedirect(!checkAuth())
}, [])
Update:
As suggested in an answer by Erfan Naghashlou, checkAuth is async function so that useEffect should be modified as:
useEffect(() => {
const x = async () => {
const checkAuthResult = await checkAuth()
setRedirect(!checkAuthResult)
}
x()
}, [])
Look at how to handle async await in useEffect
I am a newbie in React and Next JS, I want to set initial auth user data on initial load from the __app.js. But using dispatch throwing error "Invalid hook call". I know according to docs calling hooks in render function is wrong. but I am looking for an alternate solution to this.
How I can set auth data one-time so that will be available for all the pages and components.
I am including my code below.
/contexts/app.js
import { useReducer, useContext, createContext } from 'react'
const AppStateContext = createContext()
const AppDispatchContext = createContext()
const reducer = (state, action) => {
switch (action.type) {
case 'SET_AUTH': {
return state = action.payload
}
default: {
throw new Error(`Unknown action: ${action.type}`)
}
}
}
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {})
return (
<AppDispatchContext.Provider value={dispatch}>
<AppStateContext.Provider value={state}>
{children}
</AppStateContext.Provider>
</AppDispatchContext.Provider>
)
}
export const useAuth = () => useContext(AppStateContext)
export const useDispatchAuth = () => useContext(AppDispatchContext)
/_app.js
import 'bootstrap/dist/css/bootstrap.min.css'
import '../styles/globals.css'
import App from 'next/app'
import Layout from '../components/Layout'
import { mutate } from 'swr'
import { getUser } from '../requests/userApi'
import { AppProvider, useDispatchAuth } from '../contexts/app'
class MyApp extends App {
render() {
const dispatchAuth = useDispatchAuth()
const { Component, pageProps, props } = this.props
// Set initial user data
const setInitialUserData = async () => {
if (props.isServer) {
const initialData = {
loading: false,
loggedIn: (props.user) ? true : false,
user: props.user
}
const auth = await mutate('api-user', initialData, false)
dispatchAuth({
type: 'SET_AUTH',
payload: auth
})
}
}
//----------------------
// Set initial user data
setInitialUserData()
//----------------------
return (
<AppProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppProvider>
)
}
}
MyApp.getInitialProps = async (appContext) => {
let isServer = (appContext.ctx.req) ? true : false
let user = null
let userTypes = {}
// Get user server side
if (isServer) {
await getUser()
.then(response => {
let data = response.data
if (data.status == true) {
// Set user
user = data.data.user
userTypes = data.data.user_types
//---------
}
})
.catch(error => {
//
})
}
//---------------------
return {
props: {
user,
userTypes,
isServer
}
}
}
export default MyApp
I believe this is the intended use of the useEffect hook with an empty array as its second argument:
https://reactjs.org/docs/hooks-effect.html
import {useEffect} from 'react'
class MyApp extends App {
useEffect(()=> {
setInitialUserData()
},[])
render() {
...
}
}
I'm attempting to call a Graph QL Query after receiving data from my useEffect hook. I need the data from the response to use in the Query. Hooks however cannot be called conditionally. If I take away the condition however, loadedAnime will be undefined. How do I get around this restraint?
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import AnimeBanner from "../components/AnimeBanner";
import { useHttpClient } from "../Hooks/http-hook";
import { GetAnimeData } from "../GraphQLFunctions";
import { useQuery } from "#apollo/react-hooks";
import gql from "graphql-tag";
const GET_ANIME_INFO = gql`
query GetAnimeInfo($name: String!) {
Media(search: $name) {
title {
romaji
english
native
userPreferred
}
episodes
id
bannerImage
}
}
`;
const Anime = (props) => {
//Logic for getting anime data from mongoDB (episodes, name, cover image)
const { isLoading, error, sendRequest } = useHttpClient();
const [loadedAnime, setloadedAnime] = useState();
const URLTitle = useParams().URLTitle;
useEffect(() => {
const fetchAnime = async () => {
try {
const responseData = await sendRequest(
"http://localhost:5000/api/anime/" + URLTitle
);
setloadedAnime(responseData.animeData[0]);
} catch (err) {
console.log(err);
}
};
fetchAnime();
}, [sendRequest, URLTitle]);
if (isLoading || error) {
return null;
}
//Logic for getting anime data from anilist (Descriptions, tags, banner, trailer, etc.)
const { apiData, apiLoading, apiError } = useQuery(GET_ANIME_INFO, {
variables: {
name: loadedAnime.anime_name,
},
});
if (apiLoading || apiError) {
return null;
}
return <AnimeBanner src={apiData.Media.bannerImage} />;
};
export default Anime;
Short Answer: You can checkout useLazyQuery instead of useQuery.
Documentation link: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
When React mounts and renders a component that calls the useQuery hook, Apollo Client automatically executes the specified query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
The useLazyQuery hook is perfect for executing queries in response to events other than component rendering. This hook acts just like useQuery, with one key exception: when useLazyQuery is called, it does not immediately execute its associated query. Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query
import React, { useState } from 'react';
import { useLazyQuery } from '#apollo/client';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}
You can either call the query after the await finishes or you can call your query in another useEffect once you update state after your api call. In general, something like this,
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
// run your query here now that the await has resolved
}, [someDependency])
or
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
setState(result)
}, [someDependency])
useEffect(() => {
if(state.id) {
// run the query
}
}, [state.someProp])
I am trying to migrate my previously working local state to redux. Now loading available Players works just fine, but deleting will somehow stop in the playerActions.js file, where I dispatch and then return an API Call. So to further give details here are my code parts in relevance:
PlayerPage.js (Component):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadPlayers, deletePlayer } from '../../redux/actions/playerActions';
import PlayerForm from './playerform';
import PlayCard from './playercard';
import PropTypes from 'prop-types';
import { toast } from 'react-toastify';
class PlayerPage extends Component {
constructor(props) {
super(props);
this.handleDeletePlayer = this.handleDeletePlayer.bind(this);
state = {};
componentDidMount() {
const players = this.props;
players.loadPlayers().catch(err => {
alert('Loading players failed. ' + err);
});
}
handleDeletePlayer = player => {
toast.success('Player deleted');
try {
deletePlayer(player);
} catch (err) {
toast.error('Delete failed. ' + err.message, { autoClose: false });
}
};
render() {
const styles = {
margin: '20px'
};
return (
<div className="container-fluid">
<div>
<h2 style={styles}>Add Player</h2>
<div className="container-fluid">
<PlayerForm handleAddNewPlayer={this.handleAddPlayer} />
</div>
</div>
<hr></hr>
<div>
<h2 style={styles}>Available Player</h2>
<div className="container-fluid">
{this.props.players.map(player => (
<PlayCard
player={player}
key={player.id}
imageSource={`${process.env.API_URL}/${player.profileImg}`}
onDeletePlayer={this.handleDeletePlayer}
/>
))}
</div>
</div>
</div>
);
}
}
PlayerPage.propTypes = {
players: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
players: state.players
};
}
const mapDispatchToProps = {
loadPlayers,
deletePlayer
};
export default connect(mapStateToProps, mapDispatchToProps)(PlayerPage);
And the Action being called is in here:
playerActions.js:
import * as types from './actionTypes';
import * as playerApi from '../../api/playerApi';
export function loadPlayersSuccess(players) {
return { type: types.LOAD_PLAYERS_SUCCESS, players };
}
export function deletePlayerOptimistic(player) {
return { type: types.DELETE_PLAYER_OPTIMISTIC, player };
}
export function loadPlayers() {
return function(dispatch) {
return playerApi
.getAllPlayers()
.then(players => {
dispatch(loadPlayersSuccess(players));
})
.catch(err => {
throw err;
});
};
}
export function deletePlayer(player) {
console.log('Hitting deletePlayer function in playerActions');
return function(dispatch) {
dispatch(deletePlayerOptimistic(player));
return playerApi.deletePlayer(player);
};
}
The console.log is the last thing the app is hitting. But the API Call is never made though.
API Call would be:
playerApi.js:
import { handleResponse, handleError } from './apiUtils';
const axios = require('axios');
export function getAllPlayers() {
return (
axios
.get(`${process.env.API_URL}/player`)
.then(handleResponse)
.catch(handleError)
);
}
export function deletePlayer(id) {
return (
axios
.delete(`${process.env.API_URL}/player/${id}`)
.then(handleResponse)
.catch(handleError)
);
}
I was like spraying out console.log in different places and files and the last one I am hitting is the one in playerActions.js. But after hitting it the part with return function(dispatch) {} will not be executed.
So if someone could point me in a general direction I'd be more than grateful.
It looks like you are calling your action creator deletePlayer but you aren't dispatching it correctly. This is why the console.log is being called but not the method that does the request.
I'd recommend taking a look at the documentation for mapDispatchToProps to fully understand how this works. In your example, you should just need to change the call to deletePlayer in your PlayerPage component to this.props.deletePlayer() to use the action creator after it's been bound to dispatch properly.
this how the mapDispatchToProps should be:
const mapDispatchToProps = dispatch => {
return {
load: () => dispatch(loadPlayers()),
delete: () => dispatch(deletePlayer()),
}
}
then call load players with this.props.load() and delete player with this.props.delete()