Skip to content

Instantly share code, notes, and snippets.

@nikneroz
Last active December 23, 2021 20:33
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save nikneroz/c96d48563549abbe997abef5433fb111 to your computer and use it in GitHub Desktop.
Save nikneroz/c96d48563549abbe997abef5433fb111 to your computer and use it in GitHub Desktop.
Лекция №3: Redux-Saga
API_CALL_REQUEST описывает что мы начинаем процесс получения данных с API
API_CALL_SUCCESS описывает что store успешно получил данные и процесс получения данных завершен
API_CALL_FAILURE описывает что API вызов завершился ошибкой
  • 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.
// 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;
  }
}
// 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 });
  }
}
// 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();
// 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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment