Skip to content

Instantly share code, notes, and snippets.

@A1ik
Forked from nikneroz/redux_saga.md
Created June 22, 2021 10:00
Show Gist options
  • Select an option

  • Save A1ik/aa78f6b9267bc9d861587ef08ba841c2 to your computer and use it in GitHub Desktop.

Select an option

Save A1ik/aa78f6b9267bc9d861587ef08ba841c2 to your computer and use it in GitHub Desktop.

Revisions

  1. @nikneroz nikneroz revised this gist Mar 30, 2018. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions redux_saga.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@
    - API - https://redux-saga.js.org/docs/api/
    - Статья - https://hackernoon.com/redux-saga-tutorial-for-beginners-and-dog-lovers-aa69a17db645
    - Что такое генераторы - https://frontender.info/es6-in-depth-generators/
    - Абсолютные пути - https://medium.com/@ktruong008/absolute-imports-with-create-react-app-4338fbca7e3d

    ```
    API_CALL_REQUEST описывает что мы начинаем процесс получения данных с API
  2. @nikneroz nikneroz revised this gist Mar 30, 2018. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions redux_saga.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    - Руководство для начинающих - https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
    - API - https://redux-saga.js.org/docs/api/
    - Статья - https://hackernoon.com/redux-saga-tutorial-for-beginners-and-dog-lovers-aa69a17db645
    - Что такое генераторы - https://frontender.info/es6-in-depth-generators/

    ```
    API_CALL_REQUEST описывает что мы начинаем процесс получения данных с API
    @@ -46,10 +47,6 @@ export function reducer(state = initialState, action) {
    }
    ```

    Generators can pause and restart — be exited and re-entered — and actually remember the context/state of the function over time.

    Each yield in a generator basically represents an asynchronous step in a more synchronous/sequential process — somewhat like await in an async function.

    ```javascript
    // rootSaga.js
    import { takeLatest, call, put } from "redux-saga/effects";
  3. @nikneroz nikneroz revised this gist Mar 30, 2018. 1 changed file with 14 additions and 9 deletions.
    23 changes: 14 additions & 9 deletions redux_saga.md
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,19 @@
    - Руководство для начинающих - https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
    - API - https://redux-saga.js.org/docs/api/
    - Статья - https://hackernoon.com/redux-saga-tutorial-for-beginners-and-dog-lovers-aa69a17db645

    ```
    API_CALL_REQUEST says that we’re beginning the process of fetching a dog from the Dog API.
    API_CALL_SUCCESS tells the Store that we successfully retrieved a dog and are therefore no longer in the process of fetching one.
    API_CALL_FAILURE tells the Store that something went wrong with our API call. We received an error rather than a new dog
    API_CALL_REQUEST описывает что мы начинаем процесс получения данных с API
    API_CALL_SUCCESS описывает что store успешно получил данные и процесс получения данных завершен
    API_CALL_FAILURE описывает что API вызов завершился ошибкой
    ```

    - a watcherSaga is a saga that watches for an action to be dispatched to the Store, triggering a workerSaga.
    - takeLatest is a helper function provided by redux-saga that will trigger a new workerSaga when it sees an API_CALL_REQUEST, while cancelling any previously triggered workerSaga still in process to help avoid too frequent or unnecessary API calls.
    - fetchDog simply uses axios to request a random dog image from the Dog API and returns a Promise for the response.
    - workerSaga attempts to fetchDog, using another redux-saga helper function call, and stores the result (a resolved or failed Promise) in a response variable.
    - If fetchDog was a success, we extract the dog image from the response and dispatch an API_CALL_SUCCESS action with dog in the payload to the Store, using ANOTHER redux-saga helper function put.
    - If there was an error with fetchDog, we let the Store know about it by dispatching an API_CALL_FAILURE action with the error.
    - `watcherSaga` - это сага, которая следит за тем, чтобы экшен был отправлен в store, вызывая `workerSaga`.
    - `takeLatest` - это вспомогательная функция, предоставляемая redux-saga, которая будет запускать новую `workerSaga`, когда она получит `API_CALL_REQUEST`. В то же время отменяет ранее запущенную `workerSaga`, для отмены слишком частых или ненужных вызовов API.
    - `fetchDog` просто использует axios для запроса случайного изображения собаки из API и возвращает Promise для ответа.
    - `workerSaga` вызывает функцию `fetchDog`, используя другой вызов helper function redux-saga и сохраняет результат (из promise) в переменной ответа.
    - Если `fetchDog` был успешным, мы получаем изображение собаки из ответа и отправляем действие API_CALL_SUCCESS с payload в store, используя соответсвующий экшн redux-saga.
    - Если произошла ошибка в `fetchDog`, мы сообщим об этом Store, отправив действие API_CALL_FAILURE.

    ```javascript
    // rootReducer.js
    @@ -43,6 +47,7 @@ export function reducer(state = initialState, action) {
    ```

    Generators can pause and restart — be exited and re-entered — and actually remember the context/state of the function over time.

    Each yield in a generator basically represents an asynchronous step in a more synchronous/sequential process — somewhat like await in an async function.

    ```javascript
  4. @nikneroz nikneroz created this gist Mar 30, 2018.
    175 changes: 175 additions & 0 deletions redux_saga.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,175 @@
    ```
    API_CALL_REQUEST says that we’re beginning the process of fetching a dog from the Dog API.
    API_CALL_SUCCESS tells the Store that we successfully retrieved a dog and are therefore no longer in the process of fetching one.
    API_CALL_FAILURE tells the Store that something went wrong with our API call. We received an error rather than a new dog
    ```

    - a watcherSaga is a saga that watches for an action to be dispatched to the Store, triggering a workerSaga.
    - takeLatest is a helper function provided by redux-saga that will trigger a new workerSaga when it sees an API_CALL_REQUEST, while cancelling any previously triggered workerSaga still in process to help avoid too frequent or unnecessary API calls.
    - fetchDog simply uses axios to request a random dog image from the Dog API and returns a Promise for the response.
    - workerSaga attempts to fetchDog, using another redux-saga helper function call, and stores the result (a resolved or failed Promise) in a response variable.
    - If fetchDog was a success, we extract the dog image from the response and dispatch an API_CALL_SUCCESS action with dog in the payload to the Store, using ANOTHER redux-saga helper function put.
    - If there was an error with fetchDog, we let the Store know about it by dispatching an API_CALL_FAILURE action with the error.

    ```javascript
    // rootReducer.js
    // action types
    const API_CALL_REQUEST = "API_CALL_REQUEST";
    const API_CALL_SUCCESS = "API_CALL_SUCCESS";
    const API_CALL_FAILURE = "API_CALL_FAILURE";

    // reducer with initial state
    const initialState = {
    fetching: false,
    dog: null,
    error: null
    };

    export function reducer(state = initialState, action) {
    switch (action.type) {
    case API_CALL_REQUEST:
    return { ...state, fetching: true, error: null };
    break;
    case API_CALL_SUCCESS:
    return { ...state, fetching: false, dog: action.dog };
    break;
    case API_CALL_FAILURE:
    return { ...state, fetching: false, dog: null, error: action.error };
    break;
    default:
    return state;
    }
    }
    ```

    Generators can pause and restart — be exited and re-entered — and actually remember the context/state of the function over time.
    Each yield in a generator basically represents an asynchronous step in a more synchronous/sequential process — somewhat like await in an async function.

    ```javascript
    // rootSaga.js
    import { takeLatest, call, put } from "redux-saga/effects";
    import axios from "axios";

    // watcher saga: watches for actions dispatched to the store, starts worker saga
    export function* watcherSaga() {
    yield takeLatest("API_CALL_REQUEST", workerSaga);
    }

    // function that makes the api request and returns a Promise for response
    function fetchDog() {
    return axios({
    method: "get",
    url: "https://dog.ceo/api/breeds/image/random"
    });
    }

    // worker saga: makes the api call when watcher saga sees the action
    function* workerSaga() {
    try {
    const response = yield call(fetchDog);
    const dog = response.data.message;

    // dispatch a success action to the store with the new dog
    yield put({ type: "API_CALL_SUCCESS", dog });

    } catch (error) {
    // dispatch a failure action to the store with the error
    yield put({ type: "API_CALL_FAILURE", error });
    }
    }
    ```

    ```javascript
    // entry.js
    import React from "react";
    import ReactDOM from "react-dom";
    import "./index.css";
    import App from "./App";
    import registerServiceWorker from "./registerServiceWorker";

    import { createStore, applyMiddleware, compose } from "redux";
    import createSagaMiddleware from "redux-saga";
    import { Provider } from "react-redux";

    import { reducer } from "./redux";
    import { watcherSaga } from "./sagas";

    // create the saga middleware
    const sagaMiddleware = createSagaMiddleware();

    // dev tools middleware
    const reduxDevTools =
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__();

    // create a redux store with our reducer above and middleware
    let store = createStore(
    reducer,
    compose(applyMiddleware(sagaMiddleware), reduxDevTools)
    );

    // run the saga
    sagaMiddleware.run(watcherSaga);

    ReactDOM.render(
    <Provider store={store}>
    <App />
    </Provider>,
    document.getElementById("root")
    );
    registerServiceWorker();
    ```

    ```javascript
    // app.js
    import React, { Component } from "react";
    import logo from "./logo.svg";
    import "./App.css";

    import { connect } from "react-redux";

    class App extends Component {
    render() {
    const { fetching, dog, onRequestDog, error } = this.props;

    return (
    <div className="App">
    <header className="App-header">
    <img src={dog || logo} className="App-logo" alt="logo" />
    <h1 className="App-title">Welcome to Dog Saga</h1>
    </header>

    {dog ? (
    <p className="App-intro">Keep clicking for new dogs</p>
    ) : (
    <p className="App-intro">Replace the React icon with a dog!</p>
    )}

    {fetching ? (
    <button disabled>Fetching...</button>
    ) : (
    <button onClick={onRequestDog}>Request a Dog</button>
    )}

    {error && <p style={{ color: "red" }}>Uh oh - something went wrong!</p>}

    </div>
    );
    }
    }

    const mapStateToProps = state => {
    return {
    fetching: state.fetching,
    dog: state.dog,
    error: state.error
    };
    };

    const mapDispatchToProps = dispatch => {
    return {
    onRequestDog: () => dispatch({ type: "API_CALL_REQUEST" })
    };
    };

    export default connect(mapStateToProps, mapDispatchToProps)(App);
    ```