An open API service indexing awesome lists of open source software.

https://github.com/innei/jojoo

A utils and extra react hooks for Jotai v2.
https://github.com/innei/jojoo

Last synced: about 1 year ago
JSON representation

A utils and extra react hooks for Jotai v2.

Awesome Lists containing this project

README

          

# Jojoo

A utils and extra react hooks for Jotai v2.

## Install

```bash
pnpm i jojoo
```

## Usage

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

```ts
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](#createOverrideAtomsContext) for details.

Here is a simple example:

```tsx
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 (




)
}

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

const IncrementButton = () => {
const inc = useContextActions().increment
return Plus
}
```

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

```tsx
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:

```tsx
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 (



Data Id:
{id}


Data Text:
{text}



)
}

const DataActions: FC<> = (props) => {
const { testId } = props
const { setText } = useDataActions()
const text = useDataValue('text')
return (


setText(`${text} updated`)}>

)
}

// ReactNode structure like:
const App = () => (







)
```

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.

```jsx
// ReactNode structure like:
;







```

#### `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:

```tsx
interface NoteModel {
title: string
}

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

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

const App = () => {
return (
<>


>
)
}

const DataRender = () => {
const title = useCurrentNoteDataSelector((n) => n.title)
return {title}
}
```

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

```tsx
const App = () => (
<>



>
)

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

return (




)
}
```

## License

2023 © Innei, Released under the MIT License.

> [Personal Website](https://innei.in/) · GitHub [@Innei](https://github.com/innei/)