DEV Community

adriangheo
adriangheo

Posted on

04.01 - Redux and Redux Saga

App preview:
The way the app will look like

Project files:
project file structure image



src/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>SAGA-App</title>

</head>

<body>
    <div class="container">
        <div id="root"></div>
    </div>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode


src/index.js

import React from 'react';
import createSagaMiddleware from 'redux-saga';
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { logger } from 'redux-logger';
import reducer from './reduxReducers';
import App from './App';
import rootSaga from './sagas';

// Create saga middleware
const sagaMiddleware = createSagaMiddleware();

// Create a redux store with reducer and middleware (saga and logger)
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware, logger),
);

// Run the root saga
sagaMiddleware.run(rootSaga);

// Render the application
render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

// Enable Hot Module Replacement (HMR)
if (module.hot) {
  module.hot.accept(App);
}

Enter fullscreen mode Exit fullscreen mode


src/sagas.js

import { put, takeLatest, all } from 'redux-saga/effects';

// Define a generator function that fetches an image and dispatches an 'IMAGE_RECEIVED' action
function* fetchImage() {
  const response = yield fetch('https://picsum.photos/200')
    .then(response => response.blob());

  const url = URL.createObjectURL(response);
  yield put({ type: "IMAGE_RECEIVED", url: url || "Error fetching data" });
}

// Define a watcher saga that watches for 'GET_IMAGE' actions and calls fetchImage when one is dispatched
function* actionWatcher() {
  yield takeLatest('GET_IMAGE', fetchImage)
}

// Define a root saga that runs all the sagas together
export default function* rootSaga() {
  yield all([
    actionWatcher(),
  ]);
}

Enter fullscreen mode Exit fullscreen mode


src/reduxReducers.js

// Define a reducer function that initializes state and handles two action types: 'GET_IMAGE' and 'IMAGE_RECEIVED'
const reducer = (state = {}, action) => {
  switch (action.type) {
    case 'GET_IMAGE':
       // When 'GET_IMAGE' is received, set loading to true
      return { ...state, loading: true };
    case 'IMAGE_RECEIVED':
      // When 'IMAGE_RECEIVED' is received, update the image in the state and set loading to false
      return { ...state, image: action.url, loading: false }
    default:
      // For all other action types, return the current state
      return state;
  }
};

// Export the reducer function
export default reducer;
Enter fullscreen mode Exit fullscreen mode


src/reduxActions.js

// Define an action creator function getImage that returns an action of type 'GET_IMAGE'
export const getImage = () => ({
  type: 'GET_IMAGE',
});
Enter fullscreen mode Exit fullscreen mode


src/App.jsx

import React from 'react';
import Button from './containers/Button';
import ImageItem from './containers/ImageItem'
import Loading from './containers/Loading'

// Create a functional component App that renders a Button, a Loading and an ImageItem component within a div
let App = () => (
  <div>
    <Button />
    <Loading />
    <ImageItem />
  </div>
);

// Export the App component
export default App;
Enter fullscreen mode Exit fullscreen mode


src/containers/ImageItem.jsx

import React from 'react';
import { connect } from 'react-redux'

// Define a functional component ImageItem that renders an img element if the image prop is truthy
let ImageItem = ({ image }) => (
  image ?
    <article>
      <img src={image} alt="Randomly generated" />
    </article> :
    null
);

// Define a mapStateToProps function that maps state to props
const mapStateToProps = (state) => ({
  image: state.image,
})

// Connect the ImageItem component to the Redux store
ImageItem = connect(
  mapStateToProps,
  null
)(ImageItem)

// Export the connected ImageItem component
export default ImageItem;
Enter fullscreen mode Exit fullscreen mode


src/containers/Loading.jsx

import React from 'react';
import { connect } from 'react-redux'

// Define a functional component Loading that renders a loading message if the loading prop is truthy
let Loading = ({ loading }) => (
  loading ?
    <div>
      <h1>LOADING...</h1>
    </div> :
    null
);

// Define a mapStateToProps function that maps state to props
const mapStateToProps = (state) => ({
  loading: state.loading
})

// Connect the Loading component to the Redux store
Loading = connect(
  mapStateToProps,
  null
)(Loading)

// Export the connected Loading component
export default Loading;
Enter fullscreen mode Exit fullscreen mode


src/containers/Button.jsx

import React from 'react';
import { connect } from 'react-redux';
import { getImage } from '../reduxActions'

// Define a functional component Button that renders a button and dispatches the getImage action when clicked
const Button = ({getImage}) => {
  return (
    <button 
      onClick={getImage} 
    >Press to see Image</button>
  );
};

// Define a mapDispatchToProps function that maps dispatch to props
const mapDispatchToProps = {
  getImage: getImage,
};

// Connect the Button component to the Redux store
export default connect(
  null,
  mapDispatchToProps,
)(Button);
Enter fullscreen mode Exit fullscreen mode

Top comments (0)