MatnikR
MatnikR

Reputation: 191

Redux Invalid hook call

I am trying to exchange some data between components in my React App and trying to use Redux for the purpose.

I am really looking for simple functionality (storing accesstoken, retrieving accesstoken).

I have one file in folder src/reducers/currenttokenreducer.js:

const currentTokenReducer = (state, action) => {
    switch(action.type){
        case 'SETTOKEN':
            return action.payload
        case 'GETTOKEN':
            return state
        default: return null
    }
}
export default currentTokenReducer;

then I have an index.js in src/reducers/:

import currentUserReducer from './currentuser.js'
import currentTokenReducer from'./currenttoken.js'
import {combineReducers} from 'redux'

const allReducers = combineReducers({
    currentUserReducer, currentTokenReducer
})
export default allReducers

finally in index.js I have:

import React from 'react';
import b2cauth from 'react-azure-adb2c';
import ReactDOM from 'react-dom';
import jwt from 'jwt-decode'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {createStore} from 'redux';
import allReducers from './reducers';
import {Provider} from 'react-redux'

const store = createStore(allReducers);

and I guess proper encapsulation of App/ with Provider:

b2cauth.run(() => {
    ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
    serviceWorker.unregister();

In App.js I fire a gettoken function and want to store it both in local state and redux store:

componentDidMount (){


   this.gettoken();
}
async gettoken(){
const dispatch = useDispatch();
let apiurl = 'https://get.....azurewebsites.net……'
var token = await Axios.get(apiurl,{headers: { 'Content-Type': 'application/x-www-form-urlencoded' }});
this.setState({accesstoken: token.data});
dispatch (settoken(token.data));
}

settoken is defined in src/actions/:

export const settoken = (token) => {
    return {
        type: 'SETTOKEN',
        payload: token
    };
};

When I deploy it I get:

Unhandled Rejection (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 3. You might have more than one copy of React in the same app See ... for tips about how to debug and fix this problem.

And it points to this line in App.js:

const dispatch = useDispatch();

What am I doing wrong?

Upvotes: 2

Views: 2771

Answers (2)

Olivier Boiss&#233;
Olivier Boiss&#233;

Reputation: 18183

You can't use a hook inside a class component

Please read the Rules of Hooks documentation

Call Hooks from React function components.

BTW, you don't need to have a GETTOKEN action in your reducer because the token is already stored into the store.

const defaultState = {value: null};

const currentTokenReducer = (state = defaultState , action) => {
  switch(action.type){
    case 'SETTOKEN':
      return {...state, value: action.payload};
    default: 
      return state;
  }
}

export default currentTokenReducer;

Then you don't need to create a internal state inside your component because you will retrieve the token from the store

import React, {useEffect} from 'react'; 
import { useSelector, useDispatch } from 'react-redux';
import axios from 'axios';

function AppComponent() {
  const dispatch = useDispatch();
  const token = useSelector(state => state.token.value);

  useEffect(async () => { 
    const apiurl = 'https://get.....azurewebsites.net……';
    const response = await axios.get(apiurl, ...);
    dispatch({type: 'SETTOKEN', payload: response.data});
  }, []);

  return <div>{token}</div>;
}

In this example I used the hooks useDispatch and useSelector, you can find more information on theses hooks on the react-redux documentation

Upvotes: 2

Clafouti
Clafouti

Reputation: 4645

Would it be possible that you're trying to use a hook (useDispatch()) inside a Class? Because hooks don't work inside classes (see: https://reactjs.org/docs/hooks-overview.html#but-what-is-a-hook).

You can still get dispatch from your props with a good old connect(mapStateToProps)(App). (see: https://react-redux.js.org/using-react-redux/connect-mapdispatch#default-dispatch-as-a-prop)

Upvotes: 1

Related Questions