https://github.com/xizon/react-redux-fundamentals-beginner__axios-middleware-thunk
Redux +React + Axios - The process and principle of implementing SSR+ asynchronous request
https://github.com/xizon/react-redux-fundamentals-beginner__axios-middleware-thunk
axios express react redux ssr
Last synced: about 2 months ago
JSON representation
Redux +React + Axios - The process and principle of implementing SSR+ asynchronous request
- Host: GitHub
- URL: https://github.com/xizon/react-redux-fundamentals-beginner__axios-middleware-thunk
- Owner: xizon
- License: mit
- Created: 2022-02-13T04:49:33.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2022-02-13T15:48:54.000Z (about 3 years ago)
- Last Synced: 2025-01-14T12:49:59.952Z (3 months ago)
- Topics: axios, express, react, redux, ssr
- Language: JavaScript
- Homepage:
- Size: 124 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# react-redux-fundamentals-beginner__axios-middleware-thunk
[English](README.md) | [中文](README_CN.md)
---
Redux +React + Axios - The process and principle of implementing SSR+ asynchronous request
## File Structures
```sh
src/
├── reducers.js
├── createStore.js
├── getJSONData.js
├── index.js (Entry file, for client/browser)
└── App.js (React component,need to configure your own router)
```SSR Reference:
```sh
src/server/
└── server.js (For Express server-side rendering)
```## Installation And Test
**Step 1.** First, using an absolute path into your app folder directory.
```sh
$ cd /{your_directory}/react-redux-fundamentals-beginner__axios-middleware-thunk
```**Step 2.** Before doing all dev stuff make sure you have `Node 14+` installed. After that, run the following code in the main directory to install the node module dependencies.
```sh
$ sudo npm install
```**Step 3.** Run this project with `create-react-app`
```sh
$ npm run start
```**Step 4.** When you done, this will spin up a server that can be accessed at
```sh
http://localhost:3000
```---
### index.js
```js
import React from 'react'
import ReactDOM from 'react-dom'import { Provider } from 'react-redux'
//
import createStore from './createStore';
import App from './App';const store = createStore();
// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
const state = store.getState();
console.log('state: ', state);
}// Update the UI with the initial data
render();
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render);ReactDOM.render(
,
document.getElementById('root')
)```
### reducers.js
```js// Reducer 1
// Get http request asynchronously
//---------
export default function theDefaultReducer(state = [], action) {
switch (action.type) {
case 'RECEIVE_DEMO_LIST':
return action.payload;default:
return state
}
}// Reducer 2
// Counter reaction
//---------
export function countReducer(state = {count: 0}, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 }case 'DECREMENT':
return { count: state.count - 1 }
default:
return state
}}
// Reducer 3
// Fixed value
//---------
export const firstNamedReducer = (state = 1, action) => state// Reducer 4
// Fixed value
//---------
export const secondNamedReducer = (state = 2, action) => state```
### createStore.js
```js
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';import theDefaultReducer, {
countReducer,
firstNamedReducer,
secondNamedReducer
} from './reducers.js'// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
reNameDefaultReducer: theDefaultReducer,
countReducer,
firstNamedReducer,
secondNamedReducer
});export default () => {
/*
const store = createStore(rootReducer)
console.log(store.getState())Output:
{
countReducer: {count: 0},
reNameDefaultReducer : [{name: 'XXX'},{name: 'XXX'}],
firstNamedReducer : 1,
secondNamedReducer : 2
}
*/const _initialState = {}; //JSON
const _reducer = rootReducer; //Function
const _enhancer = applyMiddleware(thunk); //Function
const store = createStore(_reducer, _initialState, _enhancer);
return store;
};```
### getJSONData.js
```jsimport axios from 'axios';
const getJSONData = async (name) => {
// Wait for all the `httpRequest` functions, if they are resolved, run 'store.dispatch()'
const httpRequest = () => {
return new Promise( (resolve,reject) => {
axios({
timeout: 15000,
method: 'get',
url: `https://restcountries.com/v2/name/${name}`,
responseType: 'json'
}).then(function (response) {
resolve( response );
})
.catch(function (error) {
console.log( error );
});
});
};
const getApiData = await httpRequest();
return getApiData.data;
}export default getJSONData;
```
### App.js
```js
import React, { Component } from 'react';
import { connect } from 'react-redux';import getJSONData from './getJSONData';
class App extends Component {
constructor(props) {
super(props);
}componentDidMount() {
//from `mapDispatchToProps()`
this.props.handleGetDataFr();
}render() {
//from `mapStateToProps()`
const preloadedState_httpData = this.props.httpData;
const preloadedState_count = this.props.count;function renderList() {
if ( preloadedState_httpData !== null ) {
const list = preloadedState_httpData.map((item, index) => {
return
});
return
- {list}
} else {
return '';
}
}
function renderCounter() {
return preloadedState_count !== null ? preloadedState_count : '';
}
return (
{ this.props.handleGetDataFr(); }}>Asynchronous Fetch Data(fr)
{ this.props.handleGetDataUs(); }}> Asynchronous Fetch Data(us)
{ this.props.handleIncrement();}}>Counter ({renderCounter()})
{renderList()}
);
}
}
const mapStateToProps = (state) => {
const { reNameDefaultReducer, countReducer } = state; //Receive redux
return {
httpData: reNameDefaultReducer,
count: countReducer.count
}
};
const mapDispatchToProps = (dispatch) => {
return {
handleGetDataFr: async () => dispatch({ type: 'RECEIVE_DEMO_LIST', payload: await getJSONData('fr') }),
handleGetDataUs: async () => dispatch({ type: 'RECEIVE_DEMO_LIST', payload: await getJSONData('us') }),
handleIncrement: () => dispatch({ type: 'INCREMENT'})
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
```
### server/server.js 【Reference】
Note: You can also encapsulate `axios` as an **action creator** file and export, this **action creator** can be used in `App.js` (the component can be accessed by router, so it can also access the asynchronous request method you created in the component)
```js
import express from 'express';
import axios from 'axios';
//
import ReactDOMServer from 'react-dom/server';
import { Provider } from 'react-redux';
import { StaticRouter } from 'react-router-dom';
import createStore from '../createStore';
import App from '../App';
const port = process.env.port || 3000;
const app = express();
app.get('*', (req, res) => {
const store = createStore();
store.dispatch(async function(dispatch) {
// Wait for all the `httpRequest` functions, if they are resolved, run 'store.dispatch()'
const httpRequest = () => {
return new Promise( (resolve,reject) => {
axios({
timeout: 15000,
method: 'get',
url: `https://examples.com`,
responseType: 'json'
}).then(function (response) {
resolve( response );
})
.catch(function (error) {
console.log( error );
});
});
};
const getApiData = await httpRequest();
const action = {
type: 'RECEIVE_DEMO_LIST',
payload: getApiData.data
}
dispatch( action );
// Send the rendered html to browser.
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
//
const context = {};
// Render template
const HTMLTmpl = `
Document
window.__PRELOADED_STATE__ = {{preloadedState}};
`;
let template;
if (template != null && template != '' && typeof template != typeof undefined) {
const reactContent = ReactDOMServer.renderToString(
);
template = HTMLTmpl.replace('{{reactApp}}', reactContent)
.replace('{{preloadedState}}', JSON.stringify(store.getState()));
//do something...
}
if (context.notFound) {
res.status(404);
}
res.send(template);
});
});
app.listen(port, () => console.log(`Frontend service ok`));
```