Reputation: 15319
I'm tweaking on this code but I can't go forward because React is complaining about my hook.
// globalStateManager.js
import React, { createContext, useContext, useReducer } from 'react';
import PropTypes from 'prop-types';
export const StateContext = createContext();
export const StateProvider = ({
reducer,
initialState,
children,
}) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const getState = () => useContext(StateContext);
When I try to use the getState
function, the following error is thrown:
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 <link> for tips about how to debug and fix this problem.
How can I access the aforementioned context while being compliant to those rules?
EDIT
I'm using the getState
function in an action I created to retrieve the loggedin user, like so:
// shared/User/actions.js
import { getState } from 'shared/utils/globalStateManager';
import { SET_USER, GET_USER } from './types';
export function setUser(payload) {
const [state, dispatch] = getState();
dispatch({
type: SET_USER,
payload,
});
}
export function getUser() {
const [state, dispatch] = getState();
dispatch({
type: GET_USER,
});
}
... and then I call it into my LogIn screen:
// components/LogIn
import React, { useEffect, useState } from 'react';
import { setUser, getUser } from 'components/User/actions';
function click() {
setUser({
name: 'John',
phone: '32323232',
});
}
function useCurrentUser() {
const [currentUser, setCurrentUser] = useState(null);
useEffect(() => {
setCurrentUser(getUser());
}, []);
return currentUser;
}
function Login() {
const currentUser = useCurrentUser();
return (
<div>
{currentUser && (
<div>
<h2>User info</h2>
<ul>
<li>Name: {currentUser.name}</li>
<li>CPF: {currentUser.cpf}</li>
<li>Phone: {currentUser.phone}</li>
</ul>
</div>
)}
<button type="button" onClick={click}>
Set User state
</button>
</div>
);
}
export default Login;
Upvotes: 2
Views: 3916
Reputation: 112787
You must make sure that you use your custom hook in a function component, and a good convention is to name a custom hook with the use
prefix, e.g. useStateContext
.
const { createContext, useContext, useReducer, Fragment } = React;
const StateContext = createContext();
const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
const useStateContext = () => useContext(StateContext);
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw new Error();
}
}
function App() {
const [state, dispatch] = useStateContext();
return (
<Fragment>
Count: {state.count}
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</Fragment>
);
}
ReactDOM.render(
<StateProvider reducer={reducer} initialState={initialState}>
<App />
</StateProvider>,
document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Upvotes: 2