6
\$\begingroup\$

In my React app using Material UI, I wrote a code to manage the size of the fields of a contact form. Those fields are elements that can be added or removed depending on a configuration.

The code I wrote works as should be but looks ugly and I need help to make it better

let size = 12;
    if (useMediaQuery(theme.breakpoints.up('sm'))) {
      const hasTime = fieldsConfig.some(
        f => f.name === 'preferredContactHours',
      );
      const hasContactType = fieldsConfig.some(
        f => f.name === 'preferredContactWay',
      );
      if (name === 'phone' && (!hasTime || !hasContactType)) size = 7;
      else if (name === 'preferredContactWay' && hasTime) size = 6;
      else if (name === 'preferredContactWay' && !hasTime) size = 5;
      else if (name === 'preferredContactHours' && hasContactType) size = 6;
      else if (name === 'preferredContactHours' && !hasContactType) size = 5;
    }

The size by default is 12 so if any of the conditions are not applicable the field will be full size in the grid.

After the if the hasTime and hasContactType are about that those fields are optional so I need to see if they are present or not.

The rest is the sizing and the conditions about it regarding if I have or not the field then is a specific size.

What I need is to have it better and efficient then all that verbose way if that could be possible.

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

I think your current code looks pretty reasonable. All the logic you're typing out is required, and it's quite readable as-is. There are a few tweaks you can consider, but there could well be different opinions on whether it makes the code more elegant or not.

Array search You initially save into 2 variables whether a couple particular properties exist in the array of objects. You can make that a bit nicer by first mapping to an array of .names, then using .includes instead:

const configNames = fieldsConfig.map(config => config.name);
const hasTime = configNames.includes('preferredContactHours');
const hasContactType = configNames.includes('preferredContactWay');

Abstract into a function Your whole code snippet runs for the purpose of figuring out the size to use. Rather than having this block among whatever else may exist on that level, and rather than conditionally reassigning size in a bunch of places, you might consider a function that calculates the size instead:

const size = getSize();

(This also lets you define size with const, which is great)

Save the comparisons into variables Rather than performing the same test multiple times, you can save them into variables first, it'll help a bit:

const preferredContactWay = name === 'preferredContactWay';
const preferredContactHours = name === 'preferredContactHours';

Group the conditions that result in the same size (just an option, you may or may not like how it looks):

const getSize = () => {
  if (!useMediaQuery(theme.breakpoints.up('sm'))) {
    return 12;
  }
  const configNames = fieldsConfig.map(config => config.name);
  const hasTime = configNames.includes('preferredContactHours');
  const hasContactType = configNames.includes('preferredContactWay');

  if (name === 'phone' && (!hasTime || !hasContactType)) return 7;

  const preferredContactWay = name === 'preferredContactWay';
  const preferredContactHours = name === 'preferredContactHours';

  if (
    (preferredContactWay && hasTime) ||
    (preferredContactHours && hasContactType)
  ) {
    return 6;
  }
  else if (
    (preferredContactWay && !hasTime) ||
    (preferredContactHours && !hasContactType)
  ) {
    return 5;
  }
  return 12;
}

RE: comment, to call useMediaQuery in the caller of getSize, either do:

const size = useMediaQuery(theme.breakpoints.up('sm') ? 12 : getSize();

or

const size = getSize(useMediaQuery(theme.breakpoints.up('sm'));

altering the body of getSize as needed to check the parameter.

\$\endgroup\$
9
  • \$\begingroup\$ That solution seems nice but in my scope gives an error React Hook "useMediaQuery" is called in function "getSize" which is neither a React function component or a custom React Hook function.eslint(react-hooks/rules-of-hooks) \$\endgroup\$
    – Jakub
    Sep 14, 2020 at 19:41
  • \$\begingroup\$ Call it in the caller instead. Either use the conditional operator, or pass the result as an argument to determine the size. \$\endgroup\$ Sep 14, 2020 at 19:44
  • \$\begingroup\$ What you mean with the caller can you show an example, please \$\endgroup\$
    – Jakub
    Sep 14, 2020 at 19:46
  • \$\begingroup\$ However, this way breaks the form sizing in other ways maybe take a look at full code pastebin.com/DCkPsK3W maybe you can help me better knowing the whole thing \$\endgroup\$
    – Jakub
    Sep 14, 2020 at 19:50
  • \$\begingroup\$ What issues does it result in? I don't see anything problematic at a glance, it looks to be faithful to the original code, though maybe I missed something. \$\endgroup\$ Sep 14, 2020 at 19:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.