React Bits

repository·master·Indexed 12 days ago

https://github.com/vasanthk/react-bits

A curated collection of best practices, design patterns, and anti-patterns for React developers. It provides a reference guide for improving code quality, performance, and architecture, covering topics such as performance optimization strategies, styling methods, UX variations, and common pitfalls like direct state mutation and the misuse of findDOMNode().

Tokens
28.9K
Snippets
73
Records
87
Agent score
95%

What's inside React Bits

  1. Overview of Styling in React

    master
    React Bits explores various ideas and patterns for using CSS-in-JS within React applications. It provides resources and conceptual guidance on how to approach styling, including discussions on style composition and the performance implications of different styling methods.
  2. Overview of React Bits

    master
    React Bits is a compilation of React patterns, techniques, tips, and tricks designed to improve React development. It covers a wide range of topics including design patterns, anti-patterns to avoid, handling UX variations, performance optimization tips, styling strategies, and common React 'gotchas'.
  3. Use Higher-Order Components (HOC) for styling and interaction

    master

    Higher-Order Components (HOCs) can be used to separate interactive state logic from UI styling. This pattern is useful for isolated UI components (like a Carousel) that require minimal state for interaction. By using an HOC, you can inject state (e.g., an index) and interaction methods (e.g., previous, next) into a presentation component, allowing you to create multiple UI variations using the same logic.

    // 1. Define the HOC to manage state and logic
    const CarouselContainer = (Comp) => {
      class Carousel extends React.Component {
        constructor() {
          super();
          this.state = { index: 0 };
          this.previous = () => {
            const { index } = this.state;
            if (index > 0) this.setState({ index: index - 1 });
          };
          this.next = () => {
            const { index } = this.state;
            this.setState({ index: index + 1 });
          };
        }
    
        render() {
          return (
            <Comp
              {...this.props}
              {...this.state}
              previous={this.previous}
              next={this.next}
            />
          );
        }
      }
      return Carousel;
    };
    
    // 2. Define the UI component that receives props from the HOC
    const CarouselUI = ({ index, ...props }) => {
      // ... styling and rendering logic using 'index' ...
      return <div style={{ transform: `translateX(${index * -100}%)` }}>{props.children}</div>;
    };
    
    // 3. Wrap the UI component with the HOC
    export default CarouselContainer(CarouselUI);
  4. Implement a switching component pattern

    master

    A switching component is a pattern used to render one of many different components based on a specific prop value.

    To implement this:

    1. Create a mapping object (e.g., PAGES) where keys represent the prop values and values are the component references.
    2. In the main component, determine which component to render by looking up the prop value in the mapping object.
    3. Provide a fallback component (like a FourOhFourPage) for cases where the prop value does not match any key in the mapping.
    4. Use the selected component as a dynamic component (e.g., <Handler />) and spread the remaining props onto it.

    For better developer experience, you can use PropTypes.oneOf with the keys of your mapping object to catch invalid prop values during development.

    import HomePage from './HomePage.jsx';
    import AboutPage from './AboutPage.jsx';
    import UserPage from './UserPage.jsx';
    import FourOhFourPage from './FourOhFourPage.jsx';
    
    const PAGES = {
      home: HomePage,
      about: AboutPage,
      user: UserPage
    };
    
    const Page = (props) => {
      const Handler = PAGES[props.page] || FourOhFourPage;
    
      return <Handler {...props} />;
    };
    
    // The keys of the PAGES object can be used in the prop types to catch dev-time errors.
    Page.propTypes = {
      page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
    };
  5. Use React Fragments to avoid unnecessary DOM nodes

    master

    React Fragments allow a component to return multiple children without adding extra nodes to the DOM. This is particularly useful when you need to wrap elements that must maintain a specific parent-child relationship in the HTML structure (such as <td> elements inside a <tr>), where adding a wrapper <div> would break the HTML specification.

    When rendering a list of elements within a fragment, ensure you still provide a key prop to each child to avoid React warnings.

    render() {
        return (
          <React.Fragment>
            <td>Table Cell 1</td>
            <td>Table Cell 2</td>
          </React.Fragment>
        );
      }
  6. Configure the Feature Flag Reducer and Selector

    master

    To support this pattern, your Redux setup requires a specific reducer structure and a selector to abstract the state shape.

    Reducer Setup

    The reducer should manage an array of feature names. A common pattern is to use a BOOTSTAP action to populate this list during application initialization.

    Selector Pattern

    Always access the features state through a dedicated selector (e.g., isFeatureEnabled). This ensures that if the underlying data structure changes (e.g., moving features from a global array to a currentUser object), you only need to update the selector rather than every component.

    // features.js
    const BOOTSTAP = 'features/receive';
    
    export default function featuresReducer(state, { type, payload }) {
      if (type === BOOTSTAP) {
        return payload.features || [];
      }
      return state || [];
    }
    
    export function isFeatureEnabled(features, featureName) {
      return features.indexOf(featureName) !== -1;
    }
    
    // reducers.js
    import { combineReducers } from 'redux';
    import features, { isFeatureEnabled as isFeatureEnabledSelector } from './features';
    
    export default combineReducers({
      features
    });
    
    // The public selector used by components
    export function isFeatureEnabled({ features }, featureName) {
      return isFeatureEnabledSelector(features, featureName);
    }
  7. Use Higher Order Components (HOC) instead of Mixins

    master

    In React, avoid using Mixins for code reuse as they are considered an anti-pattern. Instead, use Higher Order Components (HOCs) to share logic between components.

    Mixins inject properties and methods directly into a component's scope, which can lead to name collisions and unpredictable behavior. HOCs, however, follow a composition pattern: they are functions that take a component and return a new component, passing shared data or logic via props. This makes the data flow explicit and easier to debug.

    // HOC Pattern: Wrap your component to inject logic/data
    var bindToCarData = function (Component) {
      return React.createClass({
        componentDidMount: function() {
          // Fetch data and call this.setState({carData: fetchedData})
        },
        render: function () {
          // Pass the state down as props to the wrapped component
          return <Component carData={ this.state.carData }/>
        }
      });
    };
    
    // Usage: Wrap the component definition
    var FirstView = bindToCarData(React.createClass({
      render: function () {
        return (
          <div>
            <AvgSellingPricesByYear country="US" dataset={this.props.carData}/>
          </div>
        );
      }
    }));
  8. Access a child component's methods from a parent component

    master

    In React, when you attach a ref to a class component, the reference points to the component instance itself rather than the underlying DOM element. This allows a parent component to call public methods defined on the child component. This pattern is useful for imperative actions like focusing an input, scrolling to an element, or triggering animations that are managed by the child.

    // Child Component: Defines the method to be exposed
    class Input extends Component {
      focus() {
        this.el.focus();
      }
    
      render() {
        return (
          <input
            ref={el => { this.el = el; }}
          />
        );
      }
    }
    
    // Parent Component: Accesses the child method via ref
    class SignInModal extends Component {
      componentDidMount() {
        // Accessing the method on the component instance
        this.InputComponent.focus();
      }
    
      render() {
        return (
          <div>
            <label>User name:</label>
            <Input
              ref={comp => { this.InputComponent = comp; }}
            />
          </div>
        );
      }
    }
  9. How One-Way Data Flow works

    master

    One-way data flow follows a unidirectional cycle:

    1. State resides in a Store: The single source of truth.
    2. Data flows down: The Store's value is passed down to components as props.
    3. Actions flow up: When a user interacts with a component, the component calls a callback function (e.g., onChange) passed from its parent. This callback triggers a method on the Store (e.g., Store.set).
    4. Store notifies subscribers: The Store updates its internal value and executes all registered _handlers.
    5. Re-render: The root component receives the notification and re-renders, passing the new data down through the component tree.

    This pattern allows you to treat React components as pure views, making the application more declarative and easier to reason about by centralizing complexity in the Store.

  10. Apply Single Responsibility Principle to React components

    master

    To ensure reusability and maintainability, React components and containers should focus on a single UI feature or functionality. Avoid monolithic components by breaking them down into smaller, specialized units.

    Example Pattern: Instead of one large form component, split it into:

    • A Shipping Address component
    • An Address container (for address-specific fields)
    • A Name container (for first and last name)
    • A Phone component
    • Specific containers for State, City, and Zip code.
  11. Separate styles from stateful logic using stateless UI components

    master

    To maintain a clean architecture, keep styles separated from components tied to application state (such as routes, views, containers, forms, and layouts).

    Instead of applying styles or classNames directly within heavy-lifting stateful components, compose them using stateless functional UI components. This pattern involves two distinct types of components:

    1. Stateful/Container Components: These handle logic, state, and data flow. They should contain no styling or CSS classes, only the composition of UI components.
    2. Stateless/Presentational Components: These are responsible for the visual representation. They receive props and apply styles (e.g., via style objects or CSS) to render the UI.
    // 1. Stateful Component (Logic only, no styles)
    class SampleComponent extends Component {
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <Heading children='Sign In'/>
            <Input
              name='username'
              value={username}
              onChange={this.handleChange}/>
            <Input
              type='password'
              name='password'
              value={password}
              onChange={this.handleChange}/>
            <Button
              type='submit'
              children='Sign In'/>
          </form>
        )
      }
    }
    
    // 2. Presentational Component (Handles the styling)
    const Button = ({ ...props }) => {
      const sx = {
        fontFamily: 'inherit',
        fontSize: 'inherit',
        fontWeight: 'bold',
        textDecoration: 'none',
        display: 'inline-block',
        margin: 0,
        paddingTop: 8,
        paddingBottom: 8,
        paddingLeft: 16,
        paddingRight: 16,
        border: 0,
        color: 'white',
        backgroundColor: 'blue',
        WebkitAppearance: 'none',
        MozAppearance: 'none'
      }
    
      return (
        <button {...props} style={sx}/>
      )
    }
  12. Use React Context for Dependency Injection

    master

    React Context provides a way to share data (like an event bus for data) that can be accessed by any component in the tree without explicitly passing props through every level.

    To use the legacy context API:

    1. Define the context provider: In a parent component, implement getChildContext() to return the data and define childContextTypes to specify the expected types of the context properties.
    2. Consume the context: In the child component, define contextTypes to declare which context properties it expects to access via this.context.
    // 1. Defining the context in a provider component
    var context = { title: 'React in patterns' };
    class App extends React.Component {
      getChildContext() {
        return context;
      }
    }
    App.childContextTypes = {
      title: PropTypes.string
    };
    
    // 2. Consuming the context in a child component
    class Inject extends React.Component {
      render() {
        var title = this.context.title;
        // ...
      }
    }
    Inject.contextTypes = {
      title: PropTypes.string
    };