Reputation: 466
I'm new to hooks and have been mildly impressed thus far, however, it seems to complain if I try to use hooks inside a function (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).
I have several common API functions inside a base file which are used to retrieve data from the API. They do NOT return anything and merely dispatch (useDispatch) to the store and the associated components using this state are updated throughout the app. I understand that is probably the reason as it's not returning a React element, but is there a way around this or a better, more "recommended" React approach? It's not the end of the world if I have to manually pass in the user and dispatch on the returned result in the parent component, but I'd rather keep it in one place and handle it all in this function so all the parent component has to do is update when the state changes.
This is the function:
export async function getCar(id) {
// It's complaining about these hooks
const { user } = useSelector(state => state.auth);
const dispatch = useDispatch();
try {
let response = await axios.get(`${baseURL}api/cars/getCar/${id}/${user.id});
if (response.data.successCode === 200) dispatch(setCar(response.data));
} catch (error) {
}
}
USAGE: const carsList = getCar("id");
Thanks all, Much appreciated.
Upvotes: 4
Views: 6707
Reputation: 659
For the situations when you have a React app running allong with some legacy code or in a microfrontend architecture you could trigger render from plain javascript by using useCallback
and a window event.
// in the hooks file
export const useForceUpdate = () => {
const [, setTriggerUpdate] = useState(0);
const update = useCallback(() => {
setTriggerUpdate((tick) => tick + 1);
}, []);
return update;
};
const handleMyOutsideCall= (
callback: () => void
) => {
window.addEventListener("myEvent", (e: CustomEvent) => {
const data = e.detail;
callback(data);
});
};
export const dispatchMyOutsideCall = (name: string) => {
const event = new CustomEvent("myEvent", {
detail: {
name: name,
},
});
window.dispatchEvent(event);
};
// import useForceUpdate from hooks file
export function MyComponent() {
const {data, setData} = React.useContext(SomeContext);
const update = useForceUpdate();
useEffect(() => {
handleMyOutsideCall(update);
}, []);
return <>App</>
}
And then in some other es module outside react scope:
// this will trigger state update on MyComponent
dispatchMyOutsideCall("My_Test")
Hope it helps
Upvotes: 0
Reputation: 1162
Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes.
You can not put hooks inside if
statements, functions, loops and they can't be nested in anything.
Hooks can only be called inside the top level of the body of a function component.
Read the official documentation.
Upvotes: 0
Reputation: 281726
Hook are different from function. Considering you want to implement a custom hook, you first need to prefix your hook name with use
Secondly hooks can't return async values unless you store it in state. Also you need to make use of useEffect function to make sure that the api request is only fired once or on change of id param.
export function useGetCar(id) {
// It's complaining about these hooks
const { user } = useSelector(state => state.auth);
const dispatch = useDispatch();
useEffect(() => {
async function myFn() {
try {
let response = await axios.get(`${baseURL}api/cars/getCar/${id}/${user.id});
if (response.data.successCode === 200) dispatch(setCar(response.data));
} catch (error) {
}
}
fn()
}, [id])
}
Also since you are just updating the state you could write another selector to get the car value from redux store
usage:
const Comp = ({id}) => {
useGetCar(id);
const car = useSelector(state => state.car);
}
Upvotes: 3