dns_nx
dns_nx

Reputation: 3953

Is there really no way to define a "component" without UI in React?

I'm struggeling already some days now. I'm searching for a way to define a React component without UI. My goal is to have several functions in one "component" (as I said without UI), which can be used by other components, but with all the benefits of useState etc. I already, thought, there is a way, by defining components like this:

export const useExample = () => {
    const [someVariable, setSomeVariable] = useState('someValue');

    const getSomething = (parameter1, parameter2) => {
        return ...
    }

    return {
        getSomething
    };
}

I thought, I could import useExample in a component and then use the function getSomething like this:

import { useExample } from 'useExample.component.js';
//...

function SomeComponent(props) {
    //...
    useEffect(() => {
        const example = useExample.getSomething(parameter1, parameter2);
    },[]);
    //...
}

...but actually this seems not be working.

Then I get an error like this:

Uncaught TypeError:

...useExample.getSomething is not a function at eval...

All docs I find is telling me, that I need to return a JSX element. But I really cannot believe that in React there is no way to do this. Am I wrong? I know, if I use plain JS, I'm able to do so, but I already tried this approach and there are many other difficulties then with combining it with React components. Did anyone have had a similar issue and found a solution to this?

Upvotes: 0

Views: 1546

Answers (2)

Quan Le
Quan Le

Reputation: 1

Beside custom hooks, you can also use Render Props pattern. It goes like this:

export const Example = ({next}) => {
    const [someVariable, setSomeVariable] = useState('someValue');

    const getSomething = (parameter1, parameter2) => {
        return ...
    }

    return next({
        getSomething
    });
}

And then, when you want to use that component, you use like this:

import { Example } from 'Example.component.js';
//...

function SomeComponent(props) {

    return <Example {...{
        next: (example) => {
            const something = example.getSomething(parameter1, parameter2);
            return <span>{something}</span>;
        },
    }}/>;

}

You can also use multiple non-UI components too: just stack them on top of each other to create a pyramid of doom like this:

import { Example } from 'Example.component.js';
//...

function SomeComponent(props) {

    return <Example1 {...{
        next: (example1) => {
            const something1 = example1.getSomething(parameter1, parameter2);
            return <Example2 {...{
                next: (example2) => {
                    const something2 = example2.getSomething(parameter1, parameter2);
                    return <span>{something1 + something2}</span>;
                },
            }}/>;
        },
    }}/>;

}

If you don't like the pyramid, you can use my lib to flatten it down:

import { Example } from 'Example.component.js';
//...

function SomeComponent(props) {

    return cs(
        ["example1", ({}, next) => <Example1 {...{next}}/>],
        ["example2", ({}, next) => <Example2 {...{next}}/>],
        ({example1, example2}) => <span>{
            example1.getSomething(parameter1, parameter2) + 
            example2.getSomething(parameter1, parameter2) 
        }</span>,
    );
}

Upvotes: 0

Arkellys
Arkellys

Reputation: 7811

What you're trying to create is a custom hook, not a component.

You cannot call a hook in a useEffect, but since it returns an object containing the function, you can store this function in a variable and then call it in the useEffect.

function SomeComponent(props) {
    const { getSomething } = useExample();

    useEffect(() => {
        const example = getSomething(parameter1, parameter2);
    },[]);

    //...
}

Also be aware that if you call this function in the useEffect, you probably want to put it in the dependency array. And depending on the rest of your code, it also means you might need to wrap getSomething in a useCallback.

Upvotes: 2

Related Questions