React Redux Saga

Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Here is an explanation of using React, Redux and Redux-Saga in a project

without an API:

Ex1:-We'll build a simple counter app that increments and decrements a value in Redux
when buttons are clicked.

First, we'll setup the React components. We'll have an App component that renders the
Counter component. The Counter component will display the count value from Redux and
have buttons to increment and decrement the count.

```jsx
// App.js

import React from 'react';


import Counter from './Counter';

function App() {
return <Counter />;
}

export default App;

// Counter.js

import React from 'react';

function Counter({count, increment, decrement}) {


return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}

export default Counter;


```

Next, we'll setup Redux with an initial state and actions/reducer to handle incrementing
and decrementing the count value.
```js
// store.js

import { createStore, applyMiddleware } from 'redux';


import createSagaMiddleware from 'redux-saga';

// Initial state
const initialState = {
count: 0
};

// Action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// Reducer
function reducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
}

// Root saga
function* rootSaga() {}

// Create saga middleware


const sagaMiddleware = createSagaMiddleware();

// Create store with middleware


const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
);

// Run saga middleware


sagaMiddleware.run(rootSaga);
export default store;
```

Then we can connect the React components to Redux using react-redux:


```jsx
// Counter.js

import { useSelector, useDispatch } from 'react-redux';

// Action creators
const increment = () => ({type: INCREMENT});
const decrement = () => ({type: DECREMENT});

function Counter() {

// Get count state from Redux store


const count = useSelector(state => state.count);

// Dispatch actions
const dispatch = useDispatch();

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
}
```

This implements a basic Redux app with React components without needing an API backend.
The count state is managed in Redux and updated when actions are dispatched in response
to button clicks. Redux-Saga is setup but not utilized since we have no async logic needed.

Ex2 .Here is how we could modify the example to integrate with an API backend:

1. Setup API client


We'll add a simple API client module that has methods for fetching and updating the
count:

```js
// api.js

const API_URL = '/api';

export async function getCount() {


const response = await fetch(`${API_URL}/count`);
return response.json();
}

export async function updateCount(count) {


const response = await fetch(`${API_URL}/count`, {
method: 'PUT',
body: JSON.stringify({ count })
});
return response.json();
}
```

2. Fetch count in component

We'll use the getCount API method in the Counter component to fetch the initial count:

```jsx
// Counter.js

import { getCount } from './api';

// ...

useEffect(() => {
getCount().then(count => dispatch({type: 'SET_COUNT', count}));
}, []);
```

3. Create data fetching saga

We'll use redux-saga to handle the async API call to fetch the count:

```js
// sagas.js

import { call, put, takeEvery } from 'redux-saga/effects';


import { getCount } from './api';

function* fetchCountSaga() {
const count = yield call(getCount);
yield put({type: 'SET_COUNT', count});
}

function* rootSaga() {
yield takeEvery('FETCH_COUNT', fetchCountSaga);
}
```

4. Dispatch fetch action

We'll dispatch the 'FETCH_COUNT' action to trigger the saga:

```js
// Counter.js

useEffect(() => {
dispatch({type: 'FETCH_COUNT'});
}, []);
```

5. Update count via API

We can call the API to update the count when increment/decrement actions are
dispatched:

```js
// sagas.js

function* incrementSaga() {
yield call(updateCount, count + 1);
yield put({type: 'FETCH_COUNT'});
}

function* decrementSaga() {
// similar to increment
}
function* rootSaga() {
// ...
yield takeEvery('INCREMENT', incrementSaga);
yield takeEvery('DECREMENT', decrementSaga);
}
```

So in summary, we fetch the initial count data from the API, use sagas to handle async
actions, and call the API to update the count when actions are dispatched. The components
listen for state changes to update the UI.

Ex3:-Okay, let's go through another more real-world example in more depth, comparing it
to concepts in JavaScript and React.

Imagine we have an e-commerce app where we need to fetch and display a list of products.
Here's how we could implement it with Redux, React and Redux-Saga:

Redux Store

The Redux store will hold the state of our app - in this case, an array of product objects:

```js
const initialState = {
products: []
};
```

This is similar to having a global state object in vanilla JavaScript that components can
reference.

Actions

We'll need action creators to define the actions that can be dispatched to the store, like
`fetchProducts`:

```js
const fetchProducts = () => ({
type: 'FETCH_PRODUCTS'
});
```
These are like event handler functions in JavaScript. They return an action object rather
than directly modifying state.

# Reducers

Reducers will update the state based on the actions:

```js
function productsReducer(state = [], action) {
switch (action.type) {
case 'FETCH_PRODUCTS_SUCCESS':
return action.products;
default:
return state;
}
}
```

Reducers are like event listeners that update state in response to actions. This is similar
to how event handlers modify state in JavaScript.

# Action Creators

We call the action creators from our components:

```jsx
// ProductList.js

import { fetchProducts } from './actions';

// dispatch action on mount


useEffect(() => {
dispatch(fetchProducts());
}, []);
```

This is like calling an event handler function in JavaScript when we want to trigger some
behavior.

# Sagas
Sagas listen for actions and handle async logic:

```js
function* fetchProductsSaga() {

// call API
const products = yield call(Api.fetchProducts);

// dispatch action with results


yield put({ type: 'FETCH_PRODUCTS_SUCCESS', products });

function* rootSaga() {
yield takeEvery('FETCH_PRODUCTS', fetchProductsSaga);
}
```

Sagas are like separate asynchronous event handlers. This keeps synchronous reducers and
action creators purely focused on state updates.

# React Components

Components render UI based on state:

```jsx
function ProductList({ products }) {
return (
<div>
{products.map(p => <Product key={p.id} product={p} />)}
</div>
);
}
```

So in summary, the data flow is:

- Components call action creators


- Action creators return actions to reducers
- Reducers update state
- Sagas listen for actions and handle async logic
- Components re-render with updated state
This unidirectional flow helps manage an application's state in a scalable way.

Here is an example of implementing CRUD operations (create, read, update, delete) with
Redux, React and an API, explaining what's happening:

# Read

To fetch a list of items from an API:

```jsx
// ItemList.js

useEffect(() => {
// dispatch fetch action
dispatch(fetchItems())
}, []);

// items saga

function* fetchItemsSaga() {

// call API
const items = yield call(API.fetchItems);

// dispatch action with items


yield put({type: 'FETCH_ITEMS_SUCCESS', items});

function* watchFetchItems() {
yield takeEvery('FETCH_ITEMS', fetchItemsSaga);
}
```

When the component mounts, it dispatches a `FETCH_ITEMS` action. The saga listens for this
and calls the API, then dispatches a `FETCH_ITEMS_SUCCESS` containing the data. The
reducer sets this as the new state.

# Create

To create a new item:


```jsx
// ItemForm.js

const onSubmit = (data) => {


// dispatch create action
dispatch(createItem(data));
}

// items saga

function* createItemSaga(action) {

// call API
yield call(API.createItem, action.payload);

// fetch updated list


yield put(fetchItems());

function* watchCreateItem() {
yield takeEvery('CREATE_ITEM', createItemSaga);
}
```

When the form is submitted, it dispatches a `CREATE_ITEM` action containing the data. The
saga calls the API to create it, then fetches an updated list from the server.

# Update

To update an existing item:

```jsx
// ItemEdit.js

const onSubmit = (data) => {


// dispatch update action
dispatch(updateItem(data));
}

// items saga

function* updateItemSaga(action) {
// call API
yield call(API.updateItem, action.payload);

// fetch updated list


yield put(fetchItems());

function* watchUpdateItem() {
yield takeEvery('UPDATE_ITEM', updateItemSaga);
}
```

Again this dispatches an action that the saga picks up. It calls the API and then refetches the
updated data.

# Delete

To delete an item:

```jsx
// ItemDelete.js

const onDelete = (id) => {


// dispatch delete action
dispatch(deleteItem(id));
}

// items saga

function* deleteItemSaga(action) {

// call API
yield call(API.deleteItem, action.payload);

// fetch updated list


yield put(fetchItems());

function* watchDeleteItem() {
yield takeEvery('DELETE_ITEM', deleteItemSaga);
}
```
The flow is similar - dispatch a DELETE action, saga calls API and refetches data.

So in summary, components dispatch CRUD actions that sagas pick up and handle by calling
the API, then refetch data to update state. This keeps the data flow unidirectional.

Here is a diagram showing how data flows in a Redux + React application with Sagas:

```mermaid
graph LR

A[React Components]
B[Action Creators]
C[Redux Store]
D[Reducers]
E[Sagas]
F[API]

A-->B
B-->C
C-->D
A-->C
D-->C
E-->F
F-->E
B-->E
E-->B
```

The flow is:

1. React Components dispatch actions using Action Creators

2. Action Creators create and return Redux actions

3. Actions are sent to the Redux Store

4. The Store passes actions to Reducers to handle synchronously

5. Reducers update the state in the Store

6. Components access the updated state from the Store

7. For async logic, Actions are also listened for by Sagas


8. Sagas call API methods and dispatch new actions when done

9. Action Creators from Sagas update the Store state

10. Components re-render with the updated state

So in summary:

- Uni-directional data flow from Components to Store


- Actions represent state changes
- Reducers sync updates to state
- Sagas handle async API calls and side effects
- Store updates propagate to Components

This pattern keeps the data and state changes easy to reason about.

There are some similarities between the architecture of a Redux+React app and a Spring Boot
web application:

- The Redux store is analogous to the model layer in Spring MVC - it represents the state of the
application.

- Redux reducers are similar to repositories/services in Spring - they contain business logic for
modifying state.

- Redux actions are like controllers in Spring MVC - they handle dispatching actions in response
to user input.

- React components are like Spring MVC controllers - they handle rendering UI and call services
to fetch/update data.

- Redux sagas/thunks are like Spring services - they encapsulate business logic and call APIs.

- React context provides dependency injection like Spring - components can access data
without prop drilling.

Some key differences:

- Redux follows strict uni-directional data flow, Spring MVC allows bi-directional.

- React performs client-side rendering, Spring does server-side.

- Redux state is immutable by convention, Spring data can be directly mutated.


- React is functional, Spring uses OOP approach.

- React is focused on UI, Spring can handle full stack.

- React abstracts away the HTTP layer, Spring exposes it for control.

So in summary, both leverage separation of concerns with layered architecture and have
similarities in the role each layer plays. But Redux enforces a stricter unidirectional data flow
and React focuses purely on the view layer as a client-side SPA framework.

You might also like