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

redux-as-repo

v1.6.0

Published

a ready to use reducer and a middleware to manage state with redux with one slice

Downloads

5

Readme

CI Publish

Redux As Repo

This library provides utility functions to deal with redux without the boilerplate.

Usage Examples on CodeSandbox

Installation

npm i redux-as-repo
# using yarn
yarn add redux-as-repo

Usage

import { repoReducer } from 'redux-as-repo';

// in your rootReducer, add the repoReducer

combineReducers({
	repository: repoReducer,
});

// or if you are using a reducer registry

reducerRegistry.register('repository', repoReducer);
import { all, fork } from 'react-sagas/effects';
import { createRepoSaga } from 'redux-as-repo';
import { axiosInstance } from 'your/axios/instance';

// in your rootSaga, create repoSaga


const handleResponse = (axiosData) => {
	if (...){
		return ...
	}
	return ...
}

const repoSaga = createRepoSaga(axiosInstance, handleResponse);

export default function* rootSaga() {
	yield all([fork(repoSaga)]);
}

createRepoSaga takes 2 arguments:

  1. axiosInstance which is required
  2. response handler: optional (if your backend has fixed format to return data, you can create a handler that returns a portion of response and store it inside the key data)
{
	"result": [],
	"status": "success",
	"errorMessage": "...",
	"errorCode": "..."
}

if you need just result, define a response handler and pass it to createRepoSaga

const handleResponse = data => {
	if (status === 'success') {
		return data.result;
	} else throw new Error(data.errorMessage);
};

Throwing an error will cause a FETCH_ERROR action to be dispatched

Common Action Creators

| actionCreator | args | saga effect | Description | | ------------------ | --------------- | ------------- | ------------------------------------------------------------------------------------------------- | | fetchInit | FetchOptions | takeEvery | Every action is handled by the repo reducer | | fetchLatest | FetchOptions | takeLatest | Only last resolved value will be taken into consideration by the repo reducer | | fetchFirst | FetchOptions | takeLeading | it blocks all upcoming actions FETCH_FIRST until the previous action is handled by repo reducer | | fetchNewInit | FetchOptions | takeEvery | same as fetchInit but creates a new namespace template for each request | | fetchClear | string | None | No Saga effect, will clear the namespace in question | | updateRepository | UpdateOptions | takeEvery | update/create new namespace with the resulting of compute method |

FetchOptions

repository slice in redux store handled by common action creators to store data.

| Property | Type | required | Description | | --------------------- | -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | url | string | Yes | url | | namespace | string | Yes | where to store the dara inside repository | | config | AxiosRequestConfig | | Axios config object (method, data, params) | | successCb | Action Creator | | Function that takes response data as an argument and returns an action (data) => ({type:'SOME_ACTION', data}) | | errorCb | Action Creator | | same as successCb but will take error as callback argument | | autoClear | boolean | | will clear the namespace after success | | skipResponseHandler | boolean | | if you are using response handler that you want to disable for this specific request , pass this option as true | | selector | Function | | A selector that returns an object with the desired to be formatted keys: state => ( { projectId: state.projectId }) const url = '/randomLink/{projectId}' projectId will be replaced therefore by the value coming from the selector |

export function fetchProjects() {
	return fetchInit(({
	 	url,
		namespace: 'projects'
	}));
}


//in your YourComponent
import { useDispatch } from 'react-redux';
import { fetchProjects } from 'path/of/your_redux_actions';

const MyComponent = () => {
	const dispatch = useDispatch()

	useEffect(() => {
	  dispatch(fetchProjects())
	},[])

	return (...)
}
  1. dispatch action of type : @repo-as-reducer/FETCH_INIT
  2. an xhr call to url with options provided
  3. response data will be stored inside repository.projects

data is stored in this format

    {
        repository: {
            projects: {
                data: [...],
                error: false,
                loading: false,
                success: true,
                trace: null,
            }
        }
    }

updateRepository

Use this common action creator for creating a new namespace or updating an existing namespace.

UpdateOptions

UpdateOptions {
	namespace: string;
	compute: (namespaceState: undefined | NamespaceState) => NewNamespaceState;
}

Usage

function updateProject(newValue) {
	return updateRepository({
		namespace: 'projects',
		compute: oldNamespaceState => ({
			...oldNamespaceState,
			key: newValue,
		}),
	});
}

Selectors

to get stored data by namespace use getData(namespace) this will create a memoized selector.

// store/YourComponent/index.ts

import { getData, getLoadingState } from 'redux-as-repo';

const dataSelector = getData(namespace);
const loadingStateSelector = getLoadingState(namespace);

// YourComponent/index.tsx
const data = useSelector(dataSelector);
const isLoading = useSelector(loadingStateSelector);

useNamespace as a custom hook

To get namespace data without using selectors, a custom hook is there for you

import { useNamespace } from 'redux-as-repo';

const { data, error, loading } = useNamespace({
	namespace: 'PROJECTS',
	onSuccess: callback,
	autoClear: true,
});

| Property | Type | required | Description | | ----------- | ---------- | ----------------- | ----------------------------------------------------------------------------- | | autoClear | boolean | default : false | clear namespace on component cleanup | | namespace | string | Yes | where namespace is saved | | onSuccess | callback | No | will be executed if namespace.success is true, data is passed to the callback |

Query Hooks Generation

To generate hooks ready to be used in the component

createNamespaceApi

| Property | Type | required | Description | | ----------- | -------- | -------- | ------------------------------------------------------------ | | namespace | string | Yes | base namespace where to store data {namespace}_{queryName} | | queries | Hook | Yes | object that holds all queries methods |

interface Hook {
	[key: string]: {
		query: (...args: any[]) => string | HookFetchOptions;
		effect?: `${FetchEffect}`;
		fetchOnMount?: boolean;
	};
}

interface HookFetchOptions {
	url: string;
	config: AxiosRequestConfig;
}

export enum FetchEffect {
	New = 'new',
	First = 'first',
	Latest = 'latest',
	Init = 'init',
}

Generated Hook ReturnType

interface HookResult extends NamespaceState {
	namespace: string;
	refetch: (...args: any[]) => void;
}

export type NamespaceState = {
	data: any;
	error: boolean;
	loading: boolean;
	success: boolean;
	trace: null | string;
	fullError: null | any;
};

Usage

import { createNamespaceApi, FetchEffect } from 'redux-as-repo';

const hooks = createNamespaceApi({
	namespace: 'todos',
	queries: {
		getTodo: {
			query: () => 'todos',
			effect: FetchEffect.First,
			fetchOnMount: true,
		},
		getTodoById: {
			query: id => `todos/${id}`,
		},
		addTodo: {
			query: data => ({
				url: '/todos',
				config: {
					method: 'post',
					data,
				},
			}),
		},
	},
});

export const { useGetTodo, useGetTodoById, useAddTodo } = hooks;

// Component.tsx
...
const [state, setState] = useState(false);
const [id, setId] = useState(1);

const { data, loading, error, refetch } = useGetTodo({
	autoClear: true,
	onSuccess: console.log,
	deps: [state],
});

const { refetch: getTodoById } = useGetTodoById(id, {
	deps: [id, state],
});

const { refetch: addTodo } = useAddTodo({ key: 'value' });
interface HookOptions {
	autoClear?: boolean;
	deps: any[];
	onSuccess: (data: NamespaceState) => any;
}