04.01 - Redux and Redux Saga
adriangheo
Posted on May 17, 2023
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>
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);
}
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(),
]);
}
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;
src/reduxActions.js
// Define an action creator function getImage that returns an action of type 'GET_IMAGE'
export const getImage = () => ({
type: 'GET_IMAGE',
});
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;
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;
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;
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);
💖 💪 🙅 🚩
adriangheo
Posted on May 17, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.