Need private packages and team management tools?Check out npm Teams »

sweet-redux-saga

1.0.5 • Public • Published

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);
  }
}

Install

npm i sweet-redux-saga

DownloadsWeekly Downloads

3

Version

1.0.5

License

MIT

Unpacked Size

177 kB

Total Files

15

Last publish

Collaborators

  • avatar