sweet-redux-saga
v1.0.5
Published
Декораторы для [react-redux](https://react-redux.js.org/) и [redux-saga](https://redux-saga.js.org/)
Downloads
14
Maintainers
Readme
Sweet redux saga 😀
Декораторы для react-redux и redux-saga
npm install sweet-redux-saga
##Предпосылки При разработке используя react-redux и redux-saga приходиться писать много однотипного кода, не относящегося к логике приложения. Данная библиотека призвана сократить этот код, и сделать его более понятный и читаемым.
##Как это выглядит ###Фабрика событий (actionsСreator) до:
const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
const SET_POSITION = 'SET_POSITION';
const CHANGE_USER = 'CHANGE_USER';
export function incrementCounter() {
return {
type: INCREMENT_COUNTER,
};
}
export function decrementCounter() {
return {
type: DECREMENT_COUNTER,
};
}
export function setPosition(x, y) {
return {
type: SET_POSITION,
payload: {
x,
y,
},
};
}
export function changeUser(userName) {
return {
type: CHANGE_USER,
payload: {
userName,
},
};
}
после:
import { actionsCreator, payload } from 'sweet-redux-saga';
@actionsCreator()
export class myActionsCreator {
//Если передаваемых параметров нет, можно объявить так
incrementCounter;
@payload()
decrementCounter;
@payload('x', 'y')
setPosition;
@payload('userName')
changeUser;
}
###Редьюсер (reducer) до:
import { SET_VISIBILITY_FILTER, ADD_TODO } from './constanst';
const initialState = {
visibilityFilter: null,
todos: [],
};
export function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.payload.filter,
});
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.payload.text,
completed: false,
},
],
});
default:
return state;
}
}
после:
import { reducer } from 'sweet-redux-saga';
import { SET_VISIBILITY_FILTER, ADD_TODO } from './constanst';
@reducer({
visibilityFilter: null,
todos: [],
})
export class TodoApp {
[SET_VISIBILITY_FILTER](state, action) {
return Object.assign({}, state, {
visibilityFilter: action.payload.filter
}}
[ADD_TODO](state, action) {
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.payload.text,
completed: false,
},
],
});
}
###Саги (sagas) до:
import { put, takeEvery, takeLatest, all, delay } from 'redux-saga/effects';
import {
INCREMENT_ASYNC,
DECREMENT_ASYNC,
INCREMENT,
DECREMENT,
} from './constants';
function* incrementAsync() {
yield delay(1000);
yield put({ type: INCREMENT });
}
function* watchIncrementAsync() {
yield takeEvery(INCREMENT_ASYNC, incrementAsync);
}
function* decrementAsync() {
yield delay(1000);
yield put({ type: DECREMENT });
}
function* watchDecrementAsync() {
yield takeLatest(DECREMENT_ASYNC, decrementAsync);
}
export default function* rootSaga() {
yield all([watchIncrementAsync(), watchDecrementAsync()]);
}
после:
import { sagas, takeEvery, takeLatest } from 'sweet-redux-saga';
import {
INCREMENT_ASYNC,
DECREMENT_ASYNC,
INCREMENT,
DECREMENT,
} from './constants';
@sagas()
export default class RootSaga {
@takeEvery([INCREMENT_ASYNC])
*incrementAsync(action) {
yield delay(1000);
yield put({ type: INCREMENT });
}
@takeLatest([DECREMENT_ASYNC])
*decrementAsync(action) {
yield delay(1000);
yield put({ type: DECREMENT });
}
}
###Фильтр событий (filterActions) до:
import { put, takeEvery, all, select } from 'redux-saga/effects';
import { INCREMENT_ASYNC, INCREMENT } from './constants';
function* incrementAsync(action) {
const state = yield select();
if (action.payload.id !== 10 || state.flag) {
return;
}
yield put({ type: INCREMENT });
}
function* watchIncrementAsync() {
yield takeEvery(INCREMENT_ASYNC, incrementAsync);
}
export default function* rootSaga() {
yield all([watchIncrementAsync()]);
}
после:
import { sagas, takeEvery } from 'sweet-redux-saga';
import { INCREMENT_ASYNC, INCREMENT } from './constants';
import { filterActions } from './SagaDecorators';
@sagas()
export default class RootSaga {
@takeEvery([INCREMENT_ASYNC])
@filterActions(({ state, payload }) => payload.id === 10 && state.flag)
*incrementAsync() {
yield put({ type: INCREMENT });
}
}
###Обработчик исключений(catchError) до:
import { put, takeEvery, all } from 'redux-saga/effects';
import { getUserSuccess, getUserError } from './userActions';
import { userServive } from './userService';
import { GET_USER_REQUEST } from './constants';
function* getUserWorker(action) {
try {
const user = yield userServive.getUser(action.payload.id);
yield put(getUserSuccess(user));
} catch (error) {
yield put(getUserError(error.message));
}
}
function* getUserWatcher() {
yield takeEvery(GET_USER_REQUEST, getUserWorker);
}
export default function* rootSaga() {
yield all([getUserWatcher()]);
}
после:
import { sagas, takeEvery } from 'sweet-redux-saga';
import { getUserSuccess, getUserError } from './userActions';
import { userServive } from './userService';
import { GET_USER_REQUEST } from './constants';
import { catchError } from './SagaDecorators';
@sagas()
export default class RootSaga {
@takeEvery([GET_USER_REQUEST])
@catchError(function*(error) {
yield put(getUserError(error.message));
})
*incrementAsync(action) {
const user = yield userServive.getUser(action.payload.id);
yield put(getUserSuccess(user));
}
}
###Конект (connect) до:
import React from 'react';
import { connect } from 'react-redux';
class Field extends React.Component {
render() {
return <div>Some template</div>;
}
}
export default connect(
(state, props) => ({}),
(dispatch, props) => ({})
)(Field);
после:
import React from 'react';
import { connect } from 'sweet-redux-saga';
@connect(
(state, props) => ({}),
(dispatch, props) => ({})
)
export default class Field extends React.Component {
render() {
return <div>Some template</div>;
}
}
##Установка ###Инициализация в хранилище (store) store.js
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducers from './rootReducer';
import rootSagas from './rootSagas';
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
export const store = createStore(rootReducers, applyMiddleware(...middlewares));
sagaMiddleware.run(rootSagas);
rootReducer.js
import { combineReducers } from 'redux';
import TodoReduces from './TodoReduces';
export function rootReducers() {
return combineReducers({
todo: new TodoReduces(),
});
}
rootSagas.js
import { all } from 'redux-saga/effects';
import TodoSagas from './TodoSagas';
export function* rootSagas() {
yield all([new TodoSagas()]);
}
Создание фабрики событий
TodoActions.js
@actionsCreator()
class TodoActions {
@payload('id')
setId;
@payload('text')
setText;
}
export const todoActions = new TodoActions();
###Создание редьюсера TodoReduces.js
import { reducer } from 'sweet-redux-saga';
import { todoActions } from 'TodoActions';
@reducer({
id: null,
text: null,
})
export default class TodoReducer {
[todoActions.setId](state, { payload }) {
return {
...state,
id: payload.id,
};
}
[todoActions.setText](state, { payload }) {
return {
...state,
text: payload.text,
};
}
}
###Создание саги TodoSagas.js
import { put } from 'redux-saga/effects';
import { sagas, takeEvery, takeLatest } from 'sweet-redux-saga';
import { todoActions } from 'TodoActions';
@sagas()
export default class TodoSagas {
@takeLatest([todoActions.setId])
*setId({ payload }) {
if (payload.id === 13) {
yield put(todoActions.setText('Плохой id, давайте поменяем'));
}
}
@takeEvery([todoActions.setText])
*logChangeText({ payload }) {
console.log(payload.text);
}
}