UNPKG

react-overrides

Version:

Helper for create reusable components through Overrides Pattern

216 lines (178 loc) 5.69 kB
# 🔮 react-overrides [![Build Status](https://travis-ci.org/ilyalesik/react-overrides.svg?branch=master)](https://travis-ci.org/ilyalesik/react-overrides) [![npm version](https://img.shields.io/npm/v/react-overrides.svg)](https://www.npmjs.com/package/react-overrides) [![npm downloads](https://img.shields.io/npm/dt/react-overrides.svg)](https://www.npmjs.com/package/react-overrides) Pass props to internal elements of React component by passing `overrides` prop. ```javascript 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`: ```javascript 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](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/n8m65940l) ## 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](https://baseui.design/) 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: ```json { "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*: ```javascript 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: ```javascript 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: ```javascript 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 ```javascript 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. ```javascript // @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](https://medium.com/@dschnr/better-reusable-react-components-with-the-overrides-pattern-9eca2339f646).