Show loader from a helper function using react context api - javascript

I am pretty new to React Context API. What I am trying to do is set a loader when I perform an API call and stop the loader after an API call is done. But if I do these dispatch actions from helper function, I am getting an error:
Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
fix this problem.
// ApiCalls.js
export const LoginService = (username, password) => {
//to show loader when api call starts
const [dispatch] = useContext(LoaderContext);
dispatch({
type: "SHOWLOADER",
payload: true
});
}
// Hello.js
export default function Hello(props) {
useEffect(() => {
LoginService();
}, []);
return(
<h2>Hello</h2>
)
}
Reproducible example.

You have two mistakes:
useContext(LoaderContext) returns a tuple (with your implementation), so you need the setter function:
// Not [dispatch]
const [,dispatch] = useContext(LoaderContext);
LoginService is actually a custom hook, and shouldn't be called inside other hook (See Rules of hooks).
import { LoaderContext } from './LoaderContext';
import {useContext, useEffect} from 'react';
// Use username,password
export const useLoginService = (username, password) => {
const [, dispatch] = useContext(LoaderContext);
useEffect(() => {
dispatch({
type: "SHOWLOADER",
payload: true,
});
}, []);
};
// Usage
export default function Hello(props) {
useLoginService();
return(
<h2>Hello</h2>
)
}
See fixed example.

You need to change your component this way:
export default function Hello(props) {
const [state, dispatch] = useContext(LoaderContext);
useEffect(() => {
dispatch({
type: "SHOWLOADER",
payload: true
});
}, []);
return(
<h2>Hello</h2>
)
}

Related

React Hook "useReducer" is called in function "fetchData" which is neither a React function component or a custom React Hook function

i am trying to use the fetch data from fetchData hook but strangely it produces the following errors->
Line 26:31: React Hook "useReducer" is called in function "fetchData" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks
Line 28:5: React Hook "useEffect" is called in function "fetchData" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks
my fetchData.js files code ->
import { useReducer, useEffect } from "react"
import axios from 'axios'
const ACTION = {
MAKE_REQUEST: 'make_request',
GET_DATA: 'get_data',
ERROR: 'error'
}
const BASE_URL = 'https://jobs.github.com/positions.json'
function reducer(state, action) {
switch (action.type) {
case ACTION.MAKE_REQUEST:
return { loading: true, jobs: [] }
case ACTION.GET_DATA:
return { ...state, loading: false, jobs: action.payload.jobs }
case ACTION.ERROR:
return { ...state, loading: false, error: action.payload.error, jobs: [] }
default:
return state
}
}
function fetchData(params, page) {
const [state, dispatch] = useReducer(reducer, { jobs: [], loading: true, error: false })
useEffect(() => {
dispatch(ACTION.MAKE_REQUEST)
axios.get(BASE_URL, {
params: { markdown: true, page: page, ...params }
}).then(res => {
dispatch({ type: ACTION.GET_DATA, payload: { jobs: res.data } })
}).catch(e => {
dispatch({ type: ACTION.ERROR, payload: { error: true } })
})
}, [params, page])
return state
}
export default fetchData
my app.js file is simple. just fetching the api data from the fetchData hook and destructuring it. Then rendering it in the return ->
import React from 'react'
import { Container } from 'react-bootstrap'
import fetchData from './fetchData'
function App() {
const { jobs, loading, error } = fetchData(null, 1)
return (
<Container>
<div>{jobs.length}</div>
{loading && <h1>loading</h1>}
{error && <h1>error</h1>}
</Container>
)
}
export default App
You need to call your function useFetchData or something similar, as per React specs, use... are valid hook names.
This is described here:
Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.
And here:
It assumes that any function starting with ”use” and a capital letter right after it is a Hook.
Custom hooks should start with the keyword use
as per https://reactjs.org/docs/hooks-rules.html.

React Hook useEffect has a missing dependency, using Redux

Should I ignore 'React Hook useEffect has a missing dependency' warning?
Usually when I am getting data from an API this is what I do:
const Component = () => {
const [data,setData] = useState([]);
const getData = () => {
//Getting data and set data code...
}
useEffect(()=>{
getData();
},[]);
}
and recently I am trying out use redux to do the same thing(getting data from API) and I got this 'React Hook useEffect has a missing dependency' warning...
action:
import {GET_POSTS} from './types';
const getPosts = () => (dispatch) => {
const url = 'https://jsonplaceholder.typicode.com/posts';
fetch(url)
.then(res => res.json())
.then(data => {
dispatch({
type: GET_POSTS,
payload: data
});
});
}
export default getPosts;
reducer:
import {GET_POSTS} from '../actions/types';
const initialState = {
posts: []
}
const postsReducer = (state = initialState, action) => {
switch(action.type){
case GET_POSTS:
return {
...state,
posts: action.payload
}
default:
return state;
}
}
export default postsReducer;
app.js:
import React, {useEffect} from 'react';
import {connect} from 'react-redux';
import Hello from './components/Hello';
import getPost from './actions/postsAction';
import './App.css';
const App = ({getPost, dispatch}) => {
useEffect(() => {
getPost();
},[]);
return (
<div className='App'>
<Hello/>
</div>
);
};
const mapdispatchtoprops = (dispatch) => ({
dispatch,
getPost: () => {
dispatch(getPost());
}
});
export default connect(null, mapdispatchtoprops)(App);
Is there a way to fix this problem, I have tried to put dispatch inside the useEffect array but the warning still shows, like this:
useEffect(() => {
getPost();
},[dispatch]);
This is the full warning: React Hook useEffect has a missing dependency: 'getPost'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Tried to remove the useEffect array but I'll get infinite loop, it'll just keeps getting the data from the api(I only need it to run once).
Should I ignore the warning? if not, whats the best practice way to handle this problem?
I never got this kind of warning before when I left the useEffect array empty but got it recently, why?
The error message is telling you what you to do. Just add getData to the dependencies array like so: [dispatch, getData]. Anything external you reference within your useEffect (like a function) should be part of the dependency list so it can trigger the effect whenever the value changes. In your case it likely won't, but React is warning you just to be safe. Hope that helps!
You may want to start thinking from a different perspective. You are apparently trying to do side effect of loading data after component got rendered. So just inject your data via redux or propagation props from parent and remove array altogether. I.e.
const Component = ({posts}) => {
const getData = () => {
//Getting data and set data code...
}
useEffect(() => {
if (!posts) {
getData();
}
});
....
}
Your posts will be loaded once and useEffect's function should only care about posts is there or not.

How can we use Redux dispatch() into useEffect? cause I am getting error [duplicate]

My folder structure:
|--App
|--Components
|--PageA.js
|--PageB.js
|--PageC.js
|--common-effects
|--useFetching.js
I am refactoring my code to fetch data from API, using react hooks.
I want to dispatch an action from useEffect in useFetching.js that is intercepted by saga middleware. The action should be dispatched only when the components(PageA, PageB, PageC) mount.
I am using redux, react-redux and redux-saga.
PageA.js:
function(props) {
useFetching(actionParams)
//....//
}
Similar code for PageB and PageC components.
I have abstracted the reusable code to fetch data in useFetching Custom hook.
useFetching.js
const useFetching = actionArgs => {
useEffect( () => {
store.dispatch(action(actionArgs)); // does not work
})
}
I don't know how to access redux dispatch in useFetching. I tried it with useReducer effect, but the sagas missed the action.
Version using react-redux hooks:
You can even cut out the connect function completely by using useDispatch from react-redux:
export default function MyComponent() {
useFetching(fetchSomething);
return <div>Doing some fetching!</div>
}
with your custom hook
import { useDispatch } from 'react-redux';
const useFetching = (someFetchActionCreator) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(someFetchActionCreator());
}, [])
}
Edit: removed dispatch from custom hook as suggested by #yonga-springfield
Note: React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
You would need to pass either bound action creators or a reference to dispatch to your hook. These would come from a connected component, same as you would normally use React-Redux:
function MyComponent(props) {
useFetching(props.fetchSomething);
return <div>Doing some fetching!</div>
}
const mapDispatch = {
fetchSomething
};
export default connect(null, mapDispatch)(MyComponent);
The hook should then call the bound action creator in the effect, which will dispatch the action accordingly.
Also, note that your current hook will re-run the effect every time the component is re-rendered, rather than just the first time. You'd need to modify the hook like this:
const useFetching = someFetchActionCreator => {
useEffect( () => {
someFetchActionCreator();
}, [])
}
This is just to bring some optimization to #Alex Hans' answer.
As per the documentation here. A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
With this in mind, we need not send a reference to the dispatch function to the useFetching hook as a parameter but rather, simply not send it and rather simply use it from within the useFetching hook with the appropriate imports.
Here's an excerpt of what I mean.
import { useDispatch } from 'react-redux';
const useFetching = (someFetchActionCreator) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(someFetchActionCreator());
}, [])
}
I can't ascertain this example will fit without errors in your codebase in your case but just trying to explain the idea/concept behind this post.
Hope this helps any future comer.
Alex Hans right decision with dispatch, but to eliminate request loops to api you can specify the dependence on dispatch ( I used Redux Toolkit )
import React, { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import axios from 'axios'
import { getItemsStart, getItemsSuccess, getItemsFailure } from '../features/itemsSlice'
const fetchItems = () => async dispatch => {
try {
dispatch(getItemsStart());
const { data } = await axios.get('url/api')
dispatch(getItemsSuccess(data))
} catch (error) {
dispatch(getItemsFailure(error))
}
}
const PageA = () => {
const dispatch = useDispatch()
const { items } = useSelector(state => state.dataSlice)
useEffect(() => {
dispatch(fetchItems())
}, [dispatch])
return (
<ul>
{items.map(item => <li>{item.name}</li>}
</ul>
)
}
export default PageA
it is important to passed dependency parameter of dispatch in the useEffect(() => {...}, [dispatch])
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
try {
await Auth.currentSession();
userHasAuthenticated(true);
} catch (e) {
if (e !== "No current user") {
alert(e);
}
}
dispatch(authentication({ type: "SET_AUTHING", payload: false }));
}

React Context : Get Data from API and call API whenever some events happens in React Component

I am new to React Context.
I need to call the API in react context to use its data throughout my react application. Also the same API needs to be called on some CRUD operation on various component of react application.
For now I am storing API data in redux which I don't want to store.
Here is what I have tried..
context.js File
import React, { useState, createContext,useEffect } from 'react';
import {getData} from './actionMethods';
const NewContext = createContext();
function newContextProvider(props) {
useEffect(async () => {
const {dataValue} = await getData()
console.log("Data " , dataValue)
}, [])
return (
<NewContext.Provider
value={{
state: {
},
actions: {
}
}}
>
{props.children}
</NewContext.Provider>
);
}
const newContextConsumer = newContext.Consumer;
export { newContextProvider, newContextConsumer, newGridContext };
actionMethods.js
export function getData() {
let config = getInstance('GET', `${prefix}/xyz/list`)
return axios(config).then(res => res.data).catch(err => {
console.log(err)
})
}
when any CRUD operation performs , I need to call the API from the context.js file to get the data from API and store in the context.
Any help would be great.
Thank You.
First we create the Context and pass it an initial value.
In order to fetch data and keep track of the returned value, we create a state inside the component. This component will manage the fetched data and pass it in the Context Provider.
To call an async function inside useEffect we need to wrap it and call it inside useEffect callback.
export const NewContext = createContext({
my_data: {} // Initial value
});
export const NewContextProvider = props => {
const [my_data, setMyData] = useState({});
useEffect(() => {
const fetchMyData = async () => {
const { dataValue } = await getData();
if (dataValue) {
setMyData(dataValue);
} else {
// There was an error fetching the data
}
};
fetchMyData();
}, []);
return (
<NewContext.Provider
value={{
my_data
}}
>
{props.children}
</NewContext.Provider>
);
};
To use this Context in a component we use the useContext hook. Remember that this component needs to be wrapped by the Provider we just created.
import React, { useContext } from "react";
import { NewContext } from "./NewContext"; // The file where the Context was created
export const MyComponent = props => {
const { my_data } = useContext(NewContext);
return //...
};
Let me know if something is not clear.

React hooks: dispatch action from useEffect

My folder structure:
|--App
|--Components
|--PageA.js
|--PageB.js
|--PageC.js
|--common-effects
|--useFetching.js
I am refactoring my code to fetch data from API, using react hooks.
I want to dispatch an action from useEffect in useFetching.js that is intercepted by saga middleware. The action should be dispatched only when the components(PageA, PageB, PageC) mount.
I am using redux, react-redux and redux-saga.
PageA.js:
function(props) {
useFetching(actionParams)
//....//
}
Similar code for PageB and PageC components.
I have abstracted the reusable code to fetch data in useFetching Custom hook.
useFetching.js
const useFetching = actionArgs => {
useEffect( () => {
store.dispatch(action(actionArgs)); // does not work
})
}
I don't know how to access redux dispatch in useFetching. I tried it with useReducer effect, but the sagas missed the action.
Version using react-redux hooks:
You can even cut out the connect function completely by using useDispatch from react-redux:
export default function MyComponent() {
useFetching(fetchSomething);
return <div>Doing some fetching!</div>
}
with your custom hook
import { useDispatch } from 'react-redux';
const useFetching = (someFetchActionCreator) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(someFetchActionCreator());
}, [])
}
Edit: removed dispatch from custom hook as suggested by #yonga-springfield
Note: React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
You would need to pass either bound action creators or a reference to dispatch to your hook. These would come from a connected component, same as you would normally use React-Redux:
function MyComponent(props) {
useFetching(props.fetchSomething);
return <div>Doing some fetching!</div>
}
const mapDispatch = {
fetchSomething
};
export default connect(null, mapDispatch)(MyComponent);
The hook should then call the bound action creator in the effect, which will dispatch the action accordingly.
Also, note that your current hook will re-run the effect every time the component is re-rendered, rather than just the first time. You'd need to modify the hook like this:
const useFetching = someFetchActionCreator => {
useEffect( () => {
someFetchActionCreator();
}, [])
}
This is just to bring some optimization to #Alex Hans' answer.
As per the documentation here. A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
With this in mind, we need not send a reference to the dispatch function to the useFetching hook as a parameter but rather, simply not send it and rather simply use it from within the useFetching hook with the appropriate imports.
Here's an excerpt of what I mean.
import { useDispatch } from 'react-redux';
const useFetching = (someFetchActionCreator) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(someFetchActionCreator());
}, [])
}
I can't ascertain this example will fit without errors in your codebase in your case but just trying to explain the idea/concept behind this post.
Hope this helps any future comer.
Alex Hans right decision with dispatch, but to eliminate request loops to api you can specify the dependence on dispatch ( I used Redux Toolkit )
import React, { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import axios from 'axios'
import { getItemsStart, getItemsSuccess, getItemsFailure } from '../features/itemsSlice'
const fetchItems = () => async dispatch => {
try {
dispatch(getItemsStart());
const { data } = await axios.get('url/api')
dispatch(getItemsSuccess(data))
} catch (error) {
dispatch(getItemsFailure(error))
}
}
const PageA = () => {
const dispatch = useDispatch()
const { items } = useSelector(state => state.dataSlice)
useEffect(() => {
dispatch(fetchItems())
}, [dispatch])
return (
<ul>
{items.map(item => <li>{item.name}</li>}
</ul>
)
}
export default PageA
it is important to passed dependency parameter of dispatch in the useEffect(() => {...}, [dispatch])
useEffect(() => {
fetchData();
}, []);
async function fetchData() {
try {
await Auth.currentSession();
userHasAuthenticated(true);
} catch (e) {
if (e !== "No current user") {
alert(e);
}
}
dispatch(authentication({ type: "SET_AUTHING", payload: false }));
}

Categories