1.6.0 • Published 3 years ago

redux-as-repo v1.6.0

Weekly downloads
-
License
ISC
Repository
github
Last release
3 years ago

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

actionCreatorargssaga effectDescription
fetchInitFetchOptionstakeEveryEvery action is handled by the repo reducer
fetchLatestFetchOptionstakeLatestOnly last resolved value will be taken into consideration by the repo reducer
fetchFirstFetchOptionstakeLeadingit blocks all upcoming actions FETCH_FIRST until the previous action is handled by repo reducer
fetchNewInitFetchOptionstakeEverysame as fetchInit but creates a new namespace template for each request
fetchClearstringNoneNo Saga effect, will clear the namespace in question
updateRepositoryUpdateOptionstakeEveryupdate/create new namespace with the resulting of compute method

FetchOptions

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

PropertyTyperequiredDescription
urlstringYesurl
namespacestringYeswhere to store the dara inside repository
configAxiosRequestConfigAxios config object (method, data, params)
successCbAction CreatorFunction that takes response data as an argument and returns an action (data) => ({type:'SOME_ACTION', data})
errorCbAction Creatorsame as successCb but will take error as callback argument
autoClearbooleanwill clear the namespace after success
skipResponseHandlerbooleanif you are using response handler that you want to disable for this specific request , pass this option as true
selectorFunctionA 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,
});
PropertyTyperequiredDescription
autoClearbooleandefault : falseclear namespace on component cleanup
namespacestringYeswhere namespace is saved
onSuccesscallbackNowill 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

PropertyTyperequiredDescription
namespacestringYesbase namespace where to store data {namespace}_{queryName}
queriesHookYesobject 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;
}
1.6.0

3 years ago

1.5.3

3 years ago

1.5.2

3 years ago

1.5.1

3 years ago

1.5.0

3 years ago

1.4.4

3 years ago

1.4.3

3 years ago

1.4.2

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.1

3 years ago

1.3.0

4 years ago

1.2.1

4 years ago

1.2.0

4 years ago

1.1.2

4 years ago

1.1.1

4 years ago

1.1.0

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago