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

jojoo

v0.2.0

Published

A utils for Jotai v2.

Downloads

2,537

Readme

Jojoo

A utils and extra react hooks for Jotai v2.

Install

pnpm i jojoo

Usage

If you want to use custom store, should setGlobalStore first.

import { setGlobalStore } from 'jojoo'
import { createStore, getDefaultStore } from 'jotai/vanilla'

// if you use custom store
const store = createStore()
setGlobalStore(store)

React Hooks

createAtomsContext

You can use createAtomsContext to implement a simple Store. Pass in an object of atoms as state. An optional second argument is an action that accepts a context object. You can access the current scope's atoms through ctx.atoms and then change the value of an atom in the current context using set.

[!NOTE] The atoms may not be the passed-in globalAtoms; they might be atoms that are overridden by a Provider. See createOverrideAtomsContext for details.

Here is a simple example:

import 'jojoo/react'

const globalAtoms = {
  aAtom: atom(0),
  bAtom: atom(false),
}

const context = createAtomsContext(globalAtoms, (ctx) => {
  const { atoms, set } = ctx
  return {
    increment: () => {
      set(atoms.aAtom, (current) => current + 1)
    },
  }
})

const [Provider, hooks, atoms, actions] = context
const [useContextAtoms, useStoreValue, useContextActions] = hooks

// Wrap `Provider` for your component

const App = () => {
  return (
    <Provider>
      <Count />
      <IncrementButton />
    </Provider>
  )
}

const Count = () => {
  const aCount = useStoreValue('aAtom')
  return <span>{aCount}</span>
}

const IncrementButton = () => {
  const inc = useContextActions().increment
  return <button onClick={inc}>Plus</button>
}

You can directly call actions returned by createAtomsContext outside of the component.

const context = createAtomsContext(globalAtoms, (ctx) => {
  const { atoms, set } = ctx
  return {
    increment: () => {
      set(atoms.aAtom, (current) => current + 1)
    },
  }
})

const [Provider, hooks, atoms, actions] = context

// not in React component.
// do something..
actions.increment()

createOverrideAtomsContext

In some cases, atoms created via createAtomsContext are used to manage data in a global store, which might be a global post state manager. However, when there exists a nested post on the page, you can't manage them globally.

At this point, you can use createOverrideAtomsContext to isolate global states, enabling state isolation between child components.

Here is a simple example:

const context = createAtomsContext(
  {
    postId: atom('0'),
    text: atom('global post text'),
  },
  ({ atoms, set }) => {
    return {
      setText: (text: string) => {
        set(atoms.text, text)
      },
    }
  },
)

const [GlobalDataProvider, [useAtoms, useDataValue, useDataActions]] = context
const OverrideProvider = createOverrideAtomsContext(context, {
  text: atom('override post text'),
  postId: atom('1'),
})

const DataRender: FC<{}> = ({ testId }) => {
  const text = useDataValue('text')
  const id = useDataValue('postId')
  return (
    <div>
      <p>
        Data Id:
        <span>{id}</span>
      </p>

      <p>
        Data Text:
        <span>{text}</span>
      </p>
    </div>
  )
}

const DataActions: FC<> = (props) => {
  const { testId } = props
  const { setText } = useDataActions()
  const text = useDataValue('text')
  return (
    <div>
      <button onClick={() => setText(`${text} updated`)}></button>
    </div>
  )
}

// ReactNode structure like:
const App = () => (
  <GlobalDataProvider>
    <DataRender />
    <DataActions />

    <OverrideProvider>
      <DataRender />
      <DataActions />
    </OverrideProvider>
  </GlobalDataProvider>
)

Child components wrapped by OverrideProvider will use the overridden atoms, isolated from global atoms. Of course, you can also use it in a nested manner.

// ReactNode structure like:
;<GlobalDataProvider>
  <DataRender />
  <DataActions />

  <OverrideProvider>
    <DataRender />
    <DataActions />

    <OverrideProvider>
      <DataRender />
      <DataActions />
    </OverrideProvider>
  </OverrideProvider>
</GlobalDataProvider>

createModelDataContext

Create a dataset context through createModelDataContext, which can manage data with Jotai, and then pass it to descendants through React.context. Utilize the feature of React.context to isolate state in multiple scenarios.

A simple usage example:

interface NoteModel {
  title: string
}

const {
  ModelDataProvider,
  ModelDataAtomProvider,
  getGlobalModelData: getModelData,
  setGlobalModelData: setModelData,
  useModelDataSelector,
  useSetModelData,
} = createModelDataProvider<NoteModel>()

export {
  ModelDataProvider as CurrentNoteDataProvider,
  ModelDataAtomProvider as CurrentNoteDataAtomProvider,
  getModelData as getCurrentNoteData,
  setModelData as setCurrentNoteData,
  useModelDataSelector as useCurrentNoteDataSelector,
  useSetModelData as useSetCurrentNoteData,
}

const App = () => {
  return (
    <>
      <CurrentNoteDataProvider data={data} />
      <DataRender />
    </>
  )
}

const DataRender = () => {
  const title = useCurrentNoteDataSelector((n) => n.title)
  return <span>{title}</span>
}

You can also use ModelDataAtomProvider for scope isolation. In this way, the internal data of ModelData in both App and AnotherData are completely independent.

const App = () => (
  <>
    <CurrentNoteDataProvider data={data} />
    <DataRender />
    <AnotherData />
  </>
)

const AnotherData = () => {
  const overrideAtom = useMemo(() => atom(null as null | NoteModel), [])

  return (
    <CurrentNoteDataAtomProvider overrideAtom={overrideAtom}>
      <CurrentNoteDataProvider data={data} />
      <DataRender />
    </CurrentNoteDataAtomProvider>
  )
}

License

2023 © Innei, Released under the MIT License.

Personal Website · GitHub @Innei