Monday Short: Redux Saga

Monday Short: Redux Saga

Valentin Günther

4. März 2024

Monday Short

Redux

React

Was ist Redux Saga

Wenn man in einer React Anwendung Redux für das State Management verwendet, ist oft der schwierigste Teil bei der Entwicklung einer App die Frage, wie man mit asynchronen Aufrufen umgeht - wie verwaltet man Netzwerkanfragen, Timeouts und andere Rückrufe, ohne die Redux-Actions und -Recuder zu verkomplizieren. 

Grundsätzlich könnte man dafür die Actions verwenden und die Requests an dieser Stelle bearbeiten. Die Verwendung von Redux allein bringt dabei jedoch wenig Flexibilität mit sich. Im Kern ist Redux nur ein state container, der synchrone Datenflüsse unterstützt: Jedes Mal, wenn eine action an den state gesendet wird, wird ein reducer aufgerufen und der Zustand wird sofort aktualisiert.

Um dieses Problem zu lösen, bietet sich die dafür entwickelte Middleware Redux Saga an.

Ohne Redux Saga: 

Action(s) -> Reducer(s)

Mit Redux Saga: 

Action(s) -> Redux Saga -> Reducer(s) 

Vorteile von Redux Saga

  • Unterstützung für asynchrone Operationen
  • Bereits vorhandene Funktionalität für beispielsweise das Abbrechen von vorherigen Saga Operationen beim Start einer Neuen  
  • Ausführen von parallelen Prozessen 

So wird die Redux Saga Middleware eingebunden

Wie hier zu sehen unterscheidet sich der grundsätzliche Aufbau nicht von der vertrauten Redux Verwendung:

const createStore = Redux.createStore;
const { Provider, connect } = ReactRedux;

// Reducer
const initialState = {
  url: "",
  loading: false,
  error: false
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "REQUESTED_API":
      return {
        url: "",
        loading: true,
        error: false
      };
    case "REQUESTED_API_SUCCEEDED":
      return {
        url: action.url,
        loading: false,
        error: false
      };
    case "REQUESTED_DOG_API":
      return {
        url: "",
        loading: false,
        error: true
      };
    default:
      return state;
  }
};

// Action Creators
const requestAPI = () => {
  return { type: "REQUESTED_API" };
};

const requestAPISuccess = (data) => {
  return { type: "REQUESTED_API_SUCCEEDED", url: data.message };
};

const requestAPIError = () => {
  return { type: "REQUESTED_API_FAILED" };
};

// Store
const sagaMiddleware = createSagaMiddleware();

const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
);
//hier wird die Middleware vom Store aufgrerufen
sagaMiddleware.run(watchFetchAPI); 

Dazu kommt nur noch die beispielhafte Saga Middleware:

// Saga
function* watchFetchAPI() {
  yield takeEvery('FETCHED_API', fetchAPIAsync);
}

function* fetchAPIAsync() {
  try {
    yield put(requestAPI());
    const data = yield call(() => {
      return fetch('https://api.example')
              .then(res => res.json())
      }
    );
    yield put(requestAPISuccess(data));
  } catch (error) {
    yield put(requestAPIError());
  }
}