react-overrides

1.4.0 • Public • Published

🔮 react-overrides

Build Status npm version npm downloads

Pass props to internal elements of React component by passing overrides prop.

export const PrimaryButton = props => (
  <CommonButton
    {...props}
    overrides={{
      Container: {
        props: {
          className: "primary_button__container", // pass className to Container element of CommonButton
          "aria-role": "button" // pass ARIA attribute
        }
      },
      Text: {
        component: BigText // replace element component to BigText
      }
    }}
  />
);

Where CommonButton was made with react-overrides:

import o from "react-overrides";
 
export const CommonButton = props => (
    <Container {...o} onClick={props.onClick}>
        <Text {...o}>{props.children}</Text>
    </Container>
);

Try at CodeSandbox:

Edit react-overrides

Why

There is a need for pass props to elements, or replace his component. Here's some examples:

  • You create UI library, and want to provide customization abilities for components. Base UI library used this approach.
  • You need unified way to pass any props (for example, ARIA attributes) to common component elements.

Installation

Install core package and babel plugin with yarn:

yarn add react-overrides
yarn add babel-plugin-react-overrides --dev

Or with npm:

npm i react-overrides --save
npm i babel-plugin-react-overrides --save-dev

Further, add react-overrides to your .babelrc configuration:

{
  "plugins": ["babel-plugin-react-overrides"]
}

Usage

Create extendable component by passing the default export from react-overrides package to the component to be extended.

Example:

import React, {useState} from "react";
import o from "react-overrides";
import {Container, Value, OptionsContainer, Option} from "...";
 
export const Select = (props) => {
    const {opened, setOpened} = useState(false);
    
    return <Container {...o} onClick={() => setOpened(!opened)}>
        <Value {...o}>{props.currentValue}</Value>
        {opened && <OptionsContainer {...o}>
            {props.values.map(({label, id}) => (
                <Option {...o} onClick={() => props.onSelect(id)} key={id}>{label}</Option>
            ))}
        </OptionsContainer>}
    </Container>
};

Extend with component through passing props of internal component:

import React from "react";
import {Select} from "./Select"
 
const BigOptionSelect = (props) => {
    return <Select {...props} overrides={{
        Option: {
            props: {
                className: "big-option-select__option",
                "aria-role": "button"
            }
        }
    }} />
}

Replace internal components

You can replace the internal component with a custom one:

import React from "react";
import {Select} from "./Select"
import {FancyGrid} from "./fancy-grid";
 
const FancyGridSelect = (props) => {
    return <Select {...props} overrides={{
        OptionsContainer: {
            component: FancyGrid
        }
    }} />
}

Access to individual props

import React from "react";
import o from "react-overrides";
import c from "classnames";
import "./button.scss";
 
const Button = props => {
    const Container = (props) => <button {...props) />;
    const Text = (props) => <span {...props) />;
    
    return (
        <Container {...o} className={c("button", o.className)} onClick={props.onClick}>
            <Text className={c("button__text", o.className)} />
                {props.children}
            </Text>
        </Input>
    );
};

Flow support

You can use ExtractOverrides type props helper for infer overrides prop type from components types.

// @flow
import * as React from "react";
import o, { type ExtractOverridesProps } from "react-overrides";
 
const Option = (props: { a: 1 | 2, b: string }) => {
    return <div>{props.b + 2 * props.a}</div>;
};
 
const Container = (props: { children?: React.Node }) => {
    return <div>{props.children}</div>;
};
 
const OverridableProps = {
    Option,
    Container
};
type TOverridesProps = ExtractOverridesProps<typeof OverridableProps>;
 
const Select = (props: { overrides: TOverridesProps }) => {
    return (
        <Container {...o}>
            {/* For access to individual prop used o().<prop>, o.<prop> throw Flow error */}
            <Option {...o} a={1} b={o().b || "x"} />
        </Container>
    );
};
 
const OverridedSelect = () => {
    return (
        <Select
            overrides={{
                Option: {
                    props: {
                        a: 1,
                    }
                }
            }}
        />
    );
};
 
// throw flow error:
const OverridedSelectWrong = () => {
    return (
        <Select
            overrides={{
                Option: {
                    props: {
                        a: 3
                    }
                }
            }}
        />
    );
};

Acknowledgements

This library inspired by article Better Reusable React Components with the Overrides Pattern.

Package Sidebar

Install

npm i react-overrides

Weekly Downloads

8

Version

1.4.0

License

MIT

Unpacked Size

27.7 kB

Total Files

15

Last publish

Collaborators

  • ilyalesik