Leo Messi
Leo Messi

Reputation: 6166

How to avoid invoking components directly in react

Having the following component:

import RadioButton from './radio-button';
import { Label, Horizontal, Vertical } from './radio-button.styles';
import { RadioOption, OptionGroup } from './radio-button.types';

export const UglyComponent = ({ label, options, horizontal, onChange }: OptionGroup) => {
  function getRenderedOptions() {
    return options.map(({ value, label }: RadioOption, index) => {
      return (
        <RadioButton
          value={value}
          label={label}
          key={index}
        />
      );
    });
  }

  const renderedOptions = horizontal ? (
    <Horizontal>{getRenderedOptions()}</Horizontal>
  ) : (
    <Vertical>{getRenderedOptions()}</Vertical>
  );

  return (
    <>
      <Label>{label}</Label>
      {renderedOptions}
    </>
  );
};

export default UglyComponent;

It kind of works, maybe the extra re-render after loading could be caused by the way its written, so instead of:

<Horizontal>{getRenderedOptions()}</Horizontal>

it could be used something like:

<Horizontal><GetRenderedOptions /></Horizontal>?

Done it like follows:

import RadioButton from './radio-button';
import { Label, Horizontal, Vertical } from './radio-button.styles';
import { RadioOption, OptionGroup } from './radio-button.types';

export const RenderedOptions = ({ options, label, onChange }: OptionGroup) => {
  return options.map(({ value, name, disabled }: RadioOption, index) => {
    const optionLabel = label.replace(/ +/g, '-');
    const optionId = `${optionLabel}-${index}`;
    return (
      <RadioButton
        value={value}
        label={label}
        key={index}
      />
    );
  });
};

export const UglyComponent = ({ label, options, horizontal, onChange }: OptionGroup) => {
  const renderedOptions = horizontal ? (
    <Horizontal>
      <RenderedOptions label={label} options={options} onChange={onChange} />
    </Horizontal>
  ) : (
    <Vertical>
      <RenderedOptions label={label} options={options} onChange={onChange} />
    </Vertical>
  );

  return (
    <>
      <Label>{label}</Label>
      {renderedOptions}
    </>
  );
};
export default UglyComponent;

It doesn't work because it returns an array of components:

'RenderedOptions' cannot be used as a JSX component. Its return type 'Element[]' is not a valid JSX element.

Upvotes: 0

Views: 19

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187004

Make it return one element instead with a fragment.

export const RenderedOptions = ({ options, label, onChange }: OptionGroup) => {
  return <>{
    options.map(({ value, name, disabled }: RadioOption, index) => {
      const optionLabel = label.replace(/ +/g, '-');
      const optionId = `${optionLabel}-${index}`;
      return (
        <RadioButton
          value={value}
          label={label}
          key={index}
        />
      );
    })
  }</>;
};

And now it's a component like any other:

<Horizontal>
  <GetRenderedOptions
    options={{ testing: 123 }}
    label="Hello"
    onChange={() => console.log('changed')}
  />
</Horizontal>

Upvotes: 1

Related Questions