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

@inventi/keep

v1.12.1

Published

[![@inventi/keep](https://img.shields.io/npm/v/@inventi/keep.svg)]() [![@inventi/keep](https://img.shields.io/npm/dw/@inventi/keep.svg)]()

Downloads

164

Readme

@inventi/keep 🇩🇪

@inventi/keep @inventi/keep

This library is a foundation stone for data loading in your React App.

  • ✅ API architecture agnostic (you can use REST, GraphQL or even smoke signals)
  • ✅ Hooks
  • ✅ All actions in redux
  • ✅ SSR fully supported
  • ✅ Typescript types included

Developer prerequisites

  • Basic React, Redux and Redux Saga knowledge

The principle aka how it works

Basically in every app you can do two actions with data:

  • ⬇️ load data
  • ↪️ trigger an action, called "mutation" (name borrowed from GraphQL)

This library will give you:

  • Redux actions/reducer/selectors for loading and mutations
  • Hooks for connection your app with data and request loading in component
  • Context Provider for client and server

This library doesn't include any support for data loading. Your job is to listen for redux actions and make API requests as sideffect!

Usage

useFetch(...args):

First argument

  • key to your redux store
  • will determine what you have to listen for in your sagas

Second, third, fourth ... args (beween first and last)

  • whatever you want to pass, usually used to add payload
  • will be included in redux action as a field "args"

Last argument

  • array of dependencies, think of it the same way as React.useEffect's dependencies ... when should data be reloaded?
  • eg. an empty array - [ ] will result in loading data just once

Very last argument (optional)

  • options object: { skip, onSkipClear }

Return value

  • { isLoading, data, error, reload }

Example:

import { useFetch, useMutate } from '@inventi/keep'

const DogListContainer = (): JSX.Element => {
  // 1. arg - 'DogList' - redux key
  // 2. and 3. arg - 'allDogs', ['name', 'image {publicUrl}'] - will be included to redux action as a field "args"
  // 4. (last) arg - [] - loads data only on mount
  const { isLoading, data, error, reload } = useFetch('DogList', 'allDogs', ['name', 'image {publicUrl}'], [])

  // you can use useFetch multiple times
  const { isLoading: isLoadingCats, data: catsData, error: errorCats } = useFetch('CatList', 'allCats', ['name', 'image {publicUrl}'], [])
  // this function "toDinosaur" receives arbitrary object as a first argument.
  // it will be included to redux action as a field "payload"
  const toDinosaur = useMutate('DogList', 'DogtoDinosaur', ['T-Rex'])
  return <DogList isLoading={isLoading} list={data} error={error} onClick={toDinosaur} />
}

Extra:

  • You can specify the type of data with generics - useFetch<DataType>(...)
  • You can also add extra options after dependencies argument - useFetch<DataType>(... , [], {skip: true, onSkipClear: false})
    • skip - determines whether fetch should be skipped.
    • onSkipClear - whether data in Redux should be cleared after skip

Example:

import { useFetch, useMutate } from '@inventi/keep'

const ExtrasExample = (): JSX.Element => {

    // ...
    // other states and such
    // ...

    const { isLoading: receiptIsLoading, data: receiptData, error: receiptError } = useFetch<Receipt>(
    'newInbound/receipt',
    'receipt',
    { id: receiptId },
    [],
    { skip: !receiptId, onSkipClear: true },
  )
}

Redux middleware for making requests:

import { actions, actionTypes, FetchAction } from '@inventi/keep'

const middleware = (store: Store) => (next: (action: FetchAction) => void) => (action: FetchAction) => {
  if (action.type === actionTypes.fetchStart) {
    // your fetch function
    handleFetch(store.dispatch, action.promise, action.key, action.args)
  }
  if (action.type === actionTypes.mutateStart) {
    // your fetch function
    handleMutation(store.dispatch, action.promise, action.key, action.args, action.payload)
  }
  return next(action)
}

Installation

This library:

yarn add @inventi/keep

Peer dependencies (necessary):

use-sse@^2.0.1 redux react-redux

App root for Client

import { createClientProvider } from '@inventi/keep'
import { Store } from 'redux'
import BareApp from './BareApp'

interface Props {
  store: Store
}

const KeepClientProvider = createClientProvider()

function App({ store }: Props) {
  return (
    <KeepClientProvider>
      <ReduxProvider store={store}>
        <BareApp /> {/** this is your app */}
      </ReduxProvider>
    </KeepClientProvider>
  )
}

export default App

Rendering on server

// example for express server
import { serverRender } from '@inventi/keep'
import express from 'express'
import App from './src/App' // your React app

server
  .get('/*', async (req, res) => {
    const store = createStore()
    // create render function that will be called by keep
    const render = (WrappedApp: ComponentType): string => {
      return ReactDOMServer.renderToString(<WrappedApp />)
    }
    // wait for data loading and rendering app
    // you'll get HTML and initial data 
    const [html, serverDataHtml] = await serverRender(render, RegisteredApp)
    const reduxState = store.getState() // grab the initial state from our Redux store

    res.send(
      `<!doctype html>
    <html lang="">
    <head>
        ${serverDataHtml}
        <script>
          window.env = ${serialize(envConfig)};
          window.__REDUX_STATE__ = ${serialize(reduxState)};
        </script>
        ...

API


// library components
import { 
  createClientProvider,
  serverRender,
  useFetch,
  useMutate,
  reducer,
  actions,
  actionTypes,
} from '@inventi/keep'
// typescript types
import  { FetchAction } from '@inventi/keep'

Thanks

Big thanks belongs to Inventi for giving us a opportunity to develop part of this open-source package as side-effect of one secret project

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT