npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

extended-components

v0.1.4

Published

Extend functional React.js components with default props, advanced local state (presets for common use cases like toggle, counter, ...) and lifecycle hooks.

Downloads

9

Readme

⚠️ As of React 16.8 it is recommended to use hooks instead of this package.

Extended Components

Extend functional React.js components with default props, advanced local state (presets for common use cases like toggle, counter, ...) and lifecycle hooks.

This package is not meant as a replacement for state management libraries like Redux, it should be used for local state only (i.e. state that is used by not really more than one component).

Problem

There are two problems that motivated me to create this package:

  1. Functional components don't support local state and lifecycle hooks.
  2. There were no presets for common local state use cases (toggles, counters, ...).

Solution

This package consists of some higher order components (HOC), which let you define default props, state and lifecycle methods for a functional component. Also you can define state presets like useCounterState or useToggleState that can be used with the withState HOC perfectly. If you define state, extended-components will pass a second argument to your functional component (as described by Andrew Clark).

This package uses HOCs and not render props. Don't get me wrong, I use render props a lot, but I think in this case HOCs have slightly advantages like a good separation of concerns and clean code.

Technically extended-components makes use of component squashing, so the original functional component will be squashed by the HOCs to improve performance.

Installation

npm install extended-components
# or
yarn add extended-components

Usage

import React from 'react';
import { compose, withState, lifecycle, pure } from 'extended-components';
import useCounterState from './useCounterState';

const enhance = compose(
  // Use state helpers to define state
  withState({
    counter: useCounterState(),
  }),
  // Use React.js lifecycle hooks with props and state
  lifecycle((props, { counter }) => {
    componentDidUpdate() {
      counter.increment();
    },
  }),
  // Component is a pure component
  pure(),
});

// Use defined state
function Component(props, state) {
  const { counter } = state;

  return (
    <div>
      <p>This component was updated {counter.count} times.</p>
      <p>
        <button onClick={() => { counter.reset() }}>Reset counter</button>
      </p>
    </div>
  );
}

export default enhance(Component);

Hint: You can use the HOCs of extended-components in conjunction with other higher order components, but thus these higher order components don't expect a functional component with two arguments, HOCs of extended-components should always be placed last, e.g.:

const enhance = compose(
  withRouter(...),
  connect(...),
  graphql(...),
  mapProps(...),
  // Use extended-components HOCs last
  withState(...),
);

Create state helpers

The purpose of this package is to enable a convient way to define state for specific use cases, but not to implement all these use cases. So it is up to you to define state helpers. A state helper is simply an object with the keys initial (for initial state) and mutators (for state mutator functions):

function useCounterState() {
  return {
    initial: {
      count: 0,
    },
    mutators: setState => ({
      increment: () => {
        setState(({ count }) => ({ count: count + 1 }));
      },
      decrement: () => {
        setState(({ count }) => ({ count: count - 1 }));
      },
      reset: () => {
        setState({ count: 0 });
      },
    }),
  };
}

Then you can reuse this state helper over and over like shown in the example above. For more examples have a look at the examples folder.

Docs

defaultProps

Defines the default props of a component.

type DefaultProps = ((defaultProps: $Shape<Props>) => void) => HOC

getDerivedStateFromProps

Defines the static lifecycle hook getDerivedStateFromProps (introduced in React 16.3). Notice that this hook returns void (in comparison to the original hook, which returns a state object), because you can update the state via the state mutators.

type GetDerivedStateFromProps = ((nextProps: Props, prevState?: State) => void) => HOC

lifecycle

Defines lifecycle hooks.

type Lifecycle = (
    (
      props: Props,
      state?: State,
    ) => {
      componentDidMount?: () => void,
      shouldComponentUpdate?: (nextProps: Props, nextState?: State) => boolean,
      componentDidUpdate?: (prevProps: Props, prevState?: State) => void,
      componentWillUnmount?: () => void,
    },
  ) => HOC;

pure

Equivalent to React.PureComponent, but instead of a basic shallow state comparison this function makes a shallow comparison for the state of every state helper.

type Pure = () => HOC;

withState

Defines state with state helpers.

type WithState = ({
    [string]: StateHelper<SubStateValues, SubStateMutators, Props>,
  }) => HOC;

statics

Defines static properties of a component.

type Statics = Object => HOC;

State helpers

As stated above you can define your own state helpers that should be of the following type:

type SetState<SubStateValues, Props> = (
  $Shape<SubStateValues> | ((SubStateValues, Props) => $Shape<SubStateValues>),
) => void;

type StateHelper<SubStateValues, SubStateMutators, Props> = {
  initial: SubStateValues,
  mutators: (SetState<SubStateValues, Props>) => SubStateMutators,
};

The definitions above might differ from the real Flow definitions, because the definitions in this section should just demonstrate what you can do with this package.

Flow support

For more information about the usage with Flow see Flow support.

Inspiration

This package is heavily inspired by Andrew Clark's great library recompose. And I think you can perfectly use recompose and extended-components together, because extended-components just mimics all class features and recompose offers a lot more utils. You can use extended-components for state and lifecycle hooks and recompose for props manipulation and more.

Future

Currently there are only two arguments that are passed to a functional component, but you can think of more arguments. So it might be that someday this package will offer an api for higher order components like withTheme, withIntl, withRouter and so on, which add more arguments to the component. Each argument for a specific use case.

License

This package is released under the MIT License.