Home > Blockchain >  Why can't I convert this to a custom React hook?
Why can't I convert this to a custom React hook?

Time:12-21

I've got a thin "service layer" in my React app that I use to push as much business logic into, and keep my components clean.

I've always used plain functions for this - passing in params and performing all the steps (reading/writing data, formatting stuff, etc.)

However, now that I've gotten a little more familiar w/ hooks, I'm wondering if it's possible to treat these functions as hooks, to make my life easier. For example, I'd like to be able to access the user object that's stored in context, whenever I need to, from this service layer.

Like so:

import { useContext } from "react";

import { AppContext } from "../component/global/app_context";

const Save = async link => {
    let { user } = useContext(AppContext); //bombs on this line!
    
    //...do stuff...
    
    return true;
};

export default Save;

I'd then call it like so:

const onEditSave = async link => {
    let updateRes = await Save(link);
    ...do other things...redirect, etc.
};

I keep getting the dreaded "Hooks can only be called inside of the body of a function component" error, so naturally I'm doing something wrong. I'm just failing to understand why this function can't be converted to a hook. How would I use it? Is this a stupid idea? If it is stupid, I'm fine w/ pulling the user and passing it into this function...it's just that I do this a lot and it seems tedious and pointless to put this in the components themselves.

Is there an easier way to access context values from a plain function, outside of those used to display React components?

CodePudding user response:

You can certainly do what you're trying to do! It's just that you need to write a custom hook. You can call React hooks in function components and inside of other hooks.

A common pattern is to return functions from your custom hook that use values from other hooks.

In your case, I think your best best is to write a custom hook that gives you access to the AppContext you've created, plus additional "utilities".

Something like this.

// Inside component/global/app_context
import React, { useContext } from "react";

const AppContext = React.createContext()

const useAppContext = () => {
    const context = useContext(AppContext);
  
    function saveUser() {
        // ...do stuff with appContext.user ...
    }

    return {
      ...context,
      saveUser,
    }
}

export default useUserContext;

Then you can access it like:

const MyComponent = () => {
    const {user, saveUser} = useAppContext()
    //...rest of component
}

You can go very far with this pattern. So much power!

  • Related