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

@fun-land/use-fun-state

v3.0.0

Published

A React architecture and library for doing fractal, compositional state in a way that is typesafe, testable, and easy to refactor.

Downloads

37

Readme

@fun-land/use-fun-state is a React.js hook for doing fractal, compositional state in a way that is type-safe, testable, and easy to refactor.

🌱 Getting Started

To work its magic, useFunState needs any React version 16.8 (needs hooks) or above. While you can use it without TypeScript, doing so is not recommended for the best experience.

npm

npm install -S @fun-land/accessor @fun-land/fun-state @fun-land/use-fun-state

yarn

yarn add @fun-land/accessor @fun-land/fun-state @fun-land/use-fun-state

🤔 Why create another React state-management library?

While React's useState has its merits, there are some pitfalls:

  • Excessive useState calls that increase management overhead.
  • Challenges in cleanly unit-testing useState dependent code.
  • Bloated functional components due to useState, making refactoring arduous.
  • The need to pass many values and callbacks to child components.

Enter useFunState. Here's what it brings to the table:

  1. Direct State Updates: Sidestep action-reducer indirection. Set the state in event handlers without guilt.
  2. Bundled State Management: Combine setters and current value, making it easy to pass them to helper functions or child components.
  3. Modular State: Easily isolate state sections and share them with functions or components.
  4. Enhanced Testability: Test components without mocking React dependencies.
  5. Type-safe with TypeScript: Ensure smooth and error-free refactoring.

While many libraries offer similar functionalities, useFunState aims for simplicity, leveraging functional programming and optics to cater to a broad spectrum of complex scenarios. Although it adopts an impure approach, it ensures that the majority of your code is testable and reduces the possibility of runtime errors.

🌟 Brief Example

import useFunState from '@fun-land/use-fun-state'

// Type definition for state.
interface CounterState {
  count: number
}

// initial value for the state
const initialCounterState: CounterState = {
  count: 0
}

export const Counter: React.FC = () => {
  // Create the FunState instance wrapping your state
  const state = useFunState(initialCounterState)
  // you can use .prop to focus a child property of a state
  const countState = state.prop('count')
  // Similar to useState you can just set directly (via .set) or apply function to the current value
  const onClick = (): void => countState.mod((count) => count + 1)

  // Extract the current value from the state with .get()
  return <button onClick={onClick}>{countState.get()}</button>
}

See fun-state-examples for a sample standalone application using vite.

❗ Considerations

When to useFunState:

  • You have more than a couple useState calls in a component.
  • When you're in a situation where you would gain benefit from redux or other state-managment libraries.
  • You want composable/modular state
  • You want to gradually try out another state management system without fully converting your app.

When not to useFunState:

  • When you just have a single useState value.
  • You're avoiding or can't use React Hooks.

💡 Tips

  • Keep your FunState Apps simple and delegate the complex logic to pure child components, using .prop() where practical.
  • Drill down into deep parts of your tree using .focus in conjunction with Accessors. See ./TodoApp or @fun-land/accessor docs for examples.
  • If child components need data from multiple places in the state tree, you can create and pass more than one FunState or just pass the root and then focus to what you need.

API

useFunState

;<State>(initialState: State) => FunState<State>

Creates an react-hooks based FunState instance with a starting state.

bindValue

;<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(
  state: FunState<string>,
  onChange?: ChangeEventHandler<T>
) => {
  value: string
  onChange: ChangeEventHandler<T>
}

Bind FunState<string> to the value property of input[type=text], textarea, or select elements.

const Name = () => {
  const state = useFunState('')
  return <input type="text" {...bindValue(state)} />
}

bindChecked

;<T extends HTMLInputElement>(state: FunState<boolean>, onChange?: ChangeEventHandler<T>) => {
  checked: boolean
  onChange: ChangeEventHandler<T>
}

Bind FunState<boolean> to the checked property of input[type=radio] or input[type=checkbox] elements.

const Cool = () => {
  const state = useFunState(false)
  return <input type="radio" {...bindChecked(state)} />
}

FunState?

See @fun-land/fun-state.

Accessor?

Used by FunState:query and FunState:focus for operating on more complex structures. See @fun-land/accessor.