Reputation: 3953
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
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
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