How can I add loader state to firebase's createUserWithEmailAndPassword method? - reactjs

I want to set a loader to my button while I call createUserWithEmailAndPassword using email & password. How can I do that?
For example in Apollo Client GraphQL, there is a loading state provided out of the box like:
const {data, loading, error} = <API call>
The given loading state automatically sets to true if the data fetching is in the works.
Is there any similar such way we can do it in firebase?

There isn't a built in function that's tied in with react state, so you would create the states yourself:
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// Whatever function is going to do the loading:
const onClick = async () => {
try {
setLoading(true);
const userCredential = await createUserWithEmailAndPassword(email, password);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}

Related

Calling a custom hook from an onClick by passing it arguments

I created a custom hook to make my api calls with axios.
When I call this hook passing it different parameters, it returns 3 states.
I created a page with a form.
When I submit this form I call a function "onSubmitform"
I would like to be able to execute this custom hook in this function.
How can I do ?
Maybe a custom hook is not suitable in this case?
-- file useAxios.js --
import { useState, useEffect } from "react";
import axios from "axios";
const useAxios = (axiosParams) => {
const [response, setResponse] = useState(undefined);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
const handleData = async (params) => {
try {
const result = await axios.request(params);
setResponse(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
handleData(axiosParams);
}, []);
return { response, error, loading };
};
export default useAxios;
-- file Page.js --
import useAxios from "../hooks/useAxios";
function Page() {
const { response } = useAxios();
const onSubmitForm = () => {
// Here I want to call the custom hook by passing it different parameters.
}
}
You can add an option to execute the request manually and avoid the fetch on mount:
const useAxios = (axiosParams, executeOnMount = true) => {
const [response, setResponse] = useState(undefined);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
const handleData = async (params) => {
try {
const result = await axios.request(params);
setResponse(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (executeOnMount) handleData(axiosParams);
}, []);
return { response, error, loading, execute: handleData };
};
Then use it:
const { response, execute } = useAxios(undefined, false);
const onSubmitForm = (data) => {
execute(params) // handleData params
}
A hook is a function (returning something or not) which should be called only when the components (re)renders.
Here you want to use it inside a callback responding to an event, which is not the same thing as the component's render.
Maybe you are just looking for a separate, "simple", function? (for example something similar to what you have in your "useEffect")

React useState loading not changing

I run into errors attempting to display data that has not been fetched yet, so.. I want to display loading until the data is fetched, but my 'setIsLoading' always returns false even when it's set to true.
Am I missing something really obvious? I'm fairly fresh to hooks.
const Pokedex = () => {
const [data, setData] = useState({});
const [isLoading, setIsLoading] = useState(false);
const pokemonId = 1;
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
const result = await axios(
`https://pokeapi.co/api/v2/pokemon/${pokemonId}`
);
setData(result.data);
};
fetchData();
setIsLoading(false);
}, [pokemonId]);
console.log("loading: ", isLoading);
You need to change isLoading state right after the fetch request completes.
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`https://pokeapi.co/api/v2/pokemon/${pokemonId}`
);
setData(result.data);
setIsLoading(false);
};
fetchData();
}, []);
Don't rely on printing stuff outside the hooks because you won't have a real feedback. You can use render method (in the return, with JSX code) or inside the hooks.
Also, since state is reset when the component is refreshed, you can rely on initializating isLoading to true. That way you just need to change it once the request is fetched.
You can check a working demo here.
First of all, If you are calling async function, you are anonymously creating Promise<void>, so after that, good way to check if promise was successfull is to use then or catch methods from Promise API.
Example solution.
const fetchData = async () => {
setIsLoading(true);
const result = await axios(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`);
setData(result.data);
};
fetchData().then(() => {
setIsLoading(false)
})
First, I think your loading state should be initialized to true.
const [isLoading, setIsLoading] = useState(true);
And then, set the loading state to false inside the async function
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`https://pokeapi.co/api/v2/pokemon/${pokemonId}`
);
setData(result.data);
setIsLoading(false);
};
fetchData();
}, [pokemonId]);

How can I reuse code when writting REST API with React Hooks?

I am going through one tutorial of REST API and react, but I want to move a step ahead and write clean code. Therefore, I want to ask you for some help, so I could reuse hooks for different API requests.
With the tutorial, I've written this example where Hooks are used for saving API request statuses and think this is a good pattern I could reuse. Basically everything except const data = await API.getItems(token['my-token']) could be used for all/most API request I want to make. How should I reuse code with these technologies when building a clean API framework?
import { useState, useEffect } from 'react';
import { API } from '../api-service';
import { useCookies } from 'react-cookie';
function useFetch() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const [token] = useCookies(['my-token']);
useEffect( ()=> {
async function fetchData() {
setLoading(true);
setError();
const data = await API.getItems(token['my-token'])
.catch( err => setError(err))
setData(data)
setLoading(false)
}
fetchData();
}, []);
return [data, loading, error]
}
export { useFetch }
BIG Thanks!
Looks like you already have a custom hook. You can just use it in whichever component you want.
Basically everything except const data = await
API.getItems(token['my-token']) could be used for all/most API request
I want to make.
You can send some arguments to your custom hook and dynamically send requests based on those parameters.
function useFetch(someArgument) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const [token] = useCookies(["my-token"]);
useEffect(() => {
async function fetchData() {
setLoading(true);
setError();
if (someArgument === 'something') {
const data = await API.getItems(token["my-token"]).catch((err) =>
setError(err)
);
} else {
// do something else
}
setData(data);
setLoading(false);
}
fetchData();
}, []);
return [data, loading, error];
}
Using the custom hook in a component:
import React from "react";
export default ({ name }) => {
const [data, loading, error] = useFetch('something');
return <h1>Hello {name}!</h1>;
};
I think you've done pretty good job, but you can improve the code by parameterizing it a bit, maybe some thing like (in case you use cookies and tokens always the same way):
import { useState, useEffect } from 'react';
import { API } from '../api-service';
import { useCookies } from 'react-cookie';
function useFetch(cookieName, promiseFactory) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const [token] = useCookies([cookieName]);
useEffect( ()=> {
async function fetchData() {
setLoading(true);
setError();
const data = await promiseFactory(token[cookieName])
.catch( err => setError(err))
setData(data)
setLoading(false)
}
fetchData();
}, []);
return [data, loading, error]
}
export { useFetch }
You can add cookieName or array of cookie names and also the promise, which obviously could be different. promiseFactory will accept the token and do its specific job (token) => Promise in order to encapsulate the token retrieval logic in the hook, that is the token[cookieName] part.
Usage would be:
function SomeComponent {
const promiseFactory = (token) => API.getItems(token);
const [data, loading, error] = useFetch('my-token', promiseFactory );
}
If the cookies logic and token retrieval is also specific only to the case you provided, you can move it outside of the hook like:
function SomeComponent {
const [token] = useCookies(['my-token']);
const promise = API.getItems(token['my-token']);
const [data, loading, error] = useFetch(promise);
}
That second approach would totally abstract away the details about how the request is constructed.

Correct dependency array for useEffect with React hooks

I am using Create-React-App and the (excellent) use-http for a custom useFetch hook. The goal is to make several API calls upon login to an account area:
const [user, setUser] = useState(null)
const [profile, setProfile] = useState(null)
const [posts, setPosts] = useState(null)
const request = useFetch('/')
const initializeAccount = async () => {
try {
const user = await request.get('api/user/')
const profile = await request.get('api/profile/')
const posts = await request.get('api/posts/')
if (user) {
setUser(user.data)
}
if (profile) {
setProfile(profile.data)
}
if (posts) {
setPosts(posts.data)
}
} catch (e) {
console.log('could not initialize account')
}
}
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
})
I have tried using [] as the dependency array, but I get a linting error saying to move initializeAccount to the dependency array. If I add it, the function runs endlessly.
What is the correct way to setup the dependency array so that this function is called one time? Also, what would be the correct way to handle abort of each of the API calls in this scenario?
My man, in order to run useEffect once for api calls, you have to do it like this:
useEffect(() => {
initializeAccount()
return () => console.log('unmount')
},[])
Hope it helps.

How to correctly call useFetch function?

I've successfully implemented a useFetch function to call an API Endpoint. It works perfectly if I add code like this to the root of a functional React component like this:
const [{ data, isLoading, isError }] = useFetch(
'http://some_api_endpoint_path'
);
export const useFetch = (url) => {
const [data, setData] = useState();
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await axios.get(url);
setData(response.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
return [{ data, isLoading, isError }];
};
But let's say I want to check if a newly entered username exists, say upon the firing of an onBlur event of an input element. When I've tried implementing this, I get this error:
React Hook "useFetch" is called in function "handleBlur" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks
I even tried this approach:
const [isChanged, setIsChanged] = useState(false);
useEffect(() => {
useFetch(
'http://some_api_endpoint_path'
);
}, [isChanged]);
But got the same error.
Then I tried this simplified version, which doesn't do anything useful but I was testing the React Hooks Rules:
useEffect(() => {
useFetch(
'http://some_api_endpoint_path'
);
}, []);
And still I got the same error.
In these last 2 cases especially, I feel that I am following the Rules of Hooks but apparently not!
What is the correct way to call useFetch in such a situation?
I suppose you call useFetch this way, right?
const onBlur = () => {
const [{ data, isLoading, isError }] = useFetch(
'http://some_api_endpoint_path'
);
...
}
If true, this is wrong. Check this link out:
🔴 Do not call in event handlers.
You may implement this way:
// Pass common initial for all fetches.
export const useFetch = (awsConfig, apiRoot, apiPathDefault) => {
const [data, setData] = useState();
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
// Just pass the variables that changes in each new fetch requisition
const fetchData = async (apiPath) => {
setIsError(false);
setIsLoading(true);
try {
const response = await axios.get(apiRoot + apiPath);
setData(response.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
useEffect(() => {
fetchData(apiRoot + apiPathDefault);
}, [awsConfig, apiRoot, apiPathDefault]);
return [{ data, isLoading, isError }, fetchData];
};
And whenever you want to fetch again, you just call fetchData:
const [{ data, isLoading, isError }, fetchData] = useFetch(API_ROOT(), appStore.awsConfig, defaultPath);
const onBlur = () => {
fetchData(newPath);
...
}
I've used the same principle that Apollo team used when created useLazyQuey (open this link and search for useLazyQuery, please). Also, note that I pass all common and immutable variables when I call the hooks and pass just the mutable ones in the single fetch.

Resources