Should I use a React Component if it always renders null, or just use a function?

I’m making a UI and came across something that made me wonder. I made a general re-usable function that fetches data and returns it in a callback, which is given to the function by whatever is calling that function. But that’s all it does, it fetches data and passes it onward. At the moment the function can take up to ~15 different parameters/props.

I made it a React Component at first, due to the feasibility of calling the function like so:


This way I can easily add and omit parameters at will. However, the SomeFunction always returns null, as its main point is returning fetched data in a callback. Should this Component be reverted to a simple function without any React in it? If so, what is the best way to approach the parameters?

My mind can quickly come up with two alternatives, the first one being positional arguments:

function someFunction(param1, param2, ... param15)

But this seems like a stretch, as I need to give many nulls or such if I want to pass something as the 15th parameter.

Another way that came to mind is to use an object:

function someFunction(options)

and then access parameters like options.param1 and options.param2.

Is the Component approach or the function approach better in this type of case? And what is the best way to handle gazillion optional parameters to a function in JS? I’m not a total noob but it feels like there are so many ways to approach things and best practices in the JS world, not to mention the ever-changing nature of the language and its derivatives.


Two suggestions:

  1. Make it a normal function that accepts its parameters as an object, probably using destructuring. (A component receives its props as an object, so that’s basically the same thing.)

  2. Return a promise rather than passing in a callback. Promises provide standard semantics that can be consumed with await in an async function and/or combined with the various promise combinators (Promise.all, Promise.race, etc.).

So for instance, if your function currently uses something that provides a promise (like fetch):

async function fetchTheInformation({param1, param2, param3 = "default for param3"}) {
    const response = await fetch(/*...*/);
    if (!response.ok) {
        throw new Error(`HTTP error ${response.status}`);
    return response.appropriateMethodHere(); // .text(), .json(), .arrayBuffer(), etc.

if it doesn’t use something that provides a promise:

function fetchTheInformation({param1, param2, param3 = "default for param3"}) {
    return new Promise((resolve, reject) => {
        // ...start the operation and call `resolve` if it works, `reject` with an `Error` if it doesn't...

In either case, the call to it can look like this in an async function:

const information = await fetchTheInformation({
    param1: "parameter 1",
    param2, // <=== If you happen to have `param2` in a variable/constant with the same name, no need to repeat it

(errors [rejections] will automatically propagate to the caller of the async function to be handled there)

or in a non-async function:

    param1: "parameter 1",
    param2, // <=== If you happen to have `param2` in a variable/constant with the same name, no need to repeat it, this is just like `param2: param,`
.then(information => {
    // ...use the information...
.catch(error => {          // Or return the promise chain to something else that will handle errors
    // ...handle/report error...

About the parameter list: I assume at least one parameter is required, but if they’re all optional (have reasonable defaults), you can do the list like this:

function example({a = "default for a", b = "default for b", c = "default for c"} = {}) {

The expressions after = within the destructuring provide defaults for those destructured parameters. The = {} at the end makes the entire parameters object optional, you with the above you can do example() or example({}) or example({a: "something"}), etc.

Leave a Reply

Your email address will not be published. Required fields are marked *