Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add style - like margin - to react component?

So, expect two simple components that I have built:

import {Input} from 'semantic-ui-react';
import {Select} from 'semantic-ui-react';

const CategoriesDropdown = ({categories, onCategorySelected, selectedCategory}) => {
    const handleChange = (e, {value})=>{
        onCategorySelected(value);
    };
    return (
        <Select placeholder="Select category" search options={categories} onChange={handleChange} value={selectedCategory} />
    );
};

const IdentifiersInput = ({identifiers, onIdentifiersChanged}) => {
    return (
        <Input placeholder="Enter identifiers..." value={identifiers} onChange={onIdentifiersChanged}/>
    );
};

Nothing fancy so far.

But now, I am building another component that displays those two in a flexbox row:

        <Box>
            <CategoriesDropdown categories={categories} selectedCategory={selectedCategoryId}
                           onCategorySelected={this.selectCategory}/>
            <IdentifiersInput identifiers={identifiers} onIdentifiersChanged={this.changeIdentifiers}/>
        </Box>

Unfortunately they are both displayed right next to each other without any margin in between.

Usually, I would just add a margin-left style to the second element, but because it is a React component, that doesn't work. Using style={{marginLeft: '20px'}} doesn't work as well, because the IdentifiersInput component doesn't use it.
I know that I can fix it by doing this: <Input style={style} ... inside the IdentifiersInput component.
However, this seems to be a very tedious way of achieving this goal. Basically, I have to add this to every single component I am writing.
I clearly must be missing something here. How am I supposed to apply such layout CSS properties to React components?

like image 923
Daniel Hilgarth Avatar asked Apr 13 '18 21:04

Daniel Hilgarth


People also ask

Can you add style to React component?

Sharing styles across many React components The style objects and the components do not have to be in the same file. We can create a separate . js file for our styles, export these styles, and then import them into the component where we want to use them.


2 Answers

I think I understand.

1) Applying CSS directly to React Components does not work--I can confirm that.

2) Passing props down to the low level elements is tedious, confirmed but viable.

Notice hasMargin prop:

<Box>
  <CategoriesDropdown
    categories={categories}
    selectedCategory={selectedCategoryId}
    onCategorySelected={this.selectCategory}
  />
  <IdentifiersInput
    identifiers={identifiers}
    onIdentifiersChanged={this.changeIdentifiers}
    hasMargin
  />
</Box>

Possible input:

const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
  return (
    <Input
      className={className}
      placeholder="Enter identifiers..."
      value={identifiers}
      onChange={onIdentifiersChanged}
      style={hasMargin ? ({ marginLeft: '0.8rem' }) : ({})}
    />
  );
};

NOTE: I do not like style as much as I like adding an additional class because classes can be adjusted via media queries:

const IdentifiersInput = ({identifiers, onIdentifiersChanged, className, hasMargin }) => {
  const inputPosition = hasMargin ? `${className} margin-sm` : className
  return (
    <Input
      className={inputPosition}
      placeholder="Enter identifiers..."
      value={identifiers}
      onChange={onIdentifiersChanged}
    />
  );
};

If you find inputPosition too verbose as shown above:

className={hasMargin ? `${className} margin-sm` : className}

3) You could accomplish it using a divider Component, sacreligious yet rapidly effective

<Box>
  <CategoriesDropdown
    categories={categories}
    selectedCategory={selectedCategoryId}
    onCategorySelected={this.selectCategory}
  />
  <div className="divider" />
  <IdentifiersInput
    identifiers={identifiers}
    onIdentifiersChanged={this.changeIdentifiers}
  />
</Box>

You can use media queries and control padding at any breakpoints if desired.

4) CSS pseudo-elements or pseudo-classes, I don't see any mention of them in answers so far.

  • MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes

  • CSS Tricks: https://css-tricks.com/pseudo-class-selectors/

Usually, when you have a random collection of DOM elements, you can calculate a way using CSS to wrangle them into the correct position. The list of available pseudo-classes is in that MDN link. It honestly helps to just look at them and reason about potential combinations.

My current issue is I don't know what is in <Box /> other than it probably has a div with display: flex; on it. If all we have to go on is that and the div is called <div className="Box">, maybe some CSS like this will fix it:

.Box {
  display: flex;
}

.Box:first-child {
  margin-right: 0.8rem;
}

This is why it is extremely important to know exactly what the surrounding elements will or can be, and exactly which CSS classes/IDs are nearby. We are basically trying to hook into something and correctly identify the left child in Box and add margin to the right of it, or target the right child and add margin to the left of it (or depending on everything, target both and split the additional margin onto both).

Remember there is also ::before and ::after. You are welcome to get creative and find a solution that involves position:relative and position: absolute and adds no markup.

I will leave my answer like that for now, because I think either you already thought about pseudo-selectors, or you will quickly find something that works :)

That or the divider is actually quite viable. The fact you can use media queries alleviates you from concern of future management or scalability of the components. I would not say the same about <div style={{}} />.

like image 119
agm1984 Avatar answered Sep 20 '22 12:09

agm1984


As your component specializes another single component it would be a good practice to pass any props your wrapper does not care for to the wrapped component. Otherwise you will loose the ability to use the api of the original <Input>component including passing styles to it:

const IdentifiersInput = ({identifiers, onIdentifiersChanged, ...props}) = (
    <Input 
        {...props}
        placeholder="Enter identifiers..." 
        value={identifiers} 
        onChange={onIdentifiersChanged}
    />
);

There may be valid cases where you explicitly want to prevent users to be able to pass props to the wrapped component but that does not look like one of those to me.

I clearly must be missing something here. How am I supposed to apply such layout CSS properties to React components?

You did not miss something. A react component has no generic way to be styled because it is no DOM element. It can have a very complicated and nested DOM representation or no representation at all. So at some point you as the designer of the component have to decided where the styles, ids and class names should be applied. In your case it is as easy as passing these props down and let the <Input> and <Select>component decide. I find that to be quite elegant rather than tedious.

like image 41
trixn Avatar answered Sep 22 '22 12:09

trixn