📜 ⬆️ ⬇️

Universal (Isomorphic) project on Koa 2.x + React + Redux + React-Router

Universal Koa

Now there is a lot of controversy about the universal (isomorphic) code, there are pros and cons.
I think that the future is universal (isomorphic) code, as it allows you to take the best from server and client rendering.

In the course of development, a good boiler-plate turned out in our team and I decided to share it. Code can be found here .

I do not want to pour water, as there is a very good tutorial on this topic:
')

About what Koa is:


Launching a project for development:

git clone https://github.com/BoryaMogila/koa_react_redux.git; npm install; npm run-script run-with-build; 

You can see the test launch at url: localhost (127.0.0.1): 4000 / app /

Launching a project for production:

 //   npm run-script build-production; //       npm run-script run-production; 

Server scripts are built, but babel-register is not used, because when using lazy-loading, when you first request a route, the return time is about two seconds due to the code transpilation.

Client scripts are collected for production builds also in gzip format. To distribute scripts, I highly recommend using nginx instead of koa-serve-static (really convenient). The server code is in the app folder, isomorphic and client in the src folder.

We write controllers for api in the koa_react_redux / app / controllers / folder:

 //koa_react_redux/app/controllers/getPostsController.js export default async function(ctx) { //        . //................ //    json ctx.body = [ { title: 'React', text: 'React is a good framework' }, { title: 'React + Redux', text: 'React + Redux is a cool thing for isomorphic apps' }, { title: 'React + Redux + React-router', text: 'React + Redux + React-router is a cool thing for isomorphic flexible apps' } ] } 

We register server routes in the koa_react_redux / app / routes / index.js file by type:

  import getPosts from '../controllers/getPostsController'; router.get('/getPosts/', getPosts); 

Universal routes are written in the file koa_react_redux / src / routes.js:

 import React from 'react'; import Route from 'react-router/lib/Route' import IndexRoute from 'react-router/lib/IndexRoute' import App from './components/App'; //  lazy-loading const getPosts = (nextState, callback) => require.ensure( [], (require) => { callback(null, require("./containers/Posts").default) } ), getPost = (nextState, callback) => require.ensure( [], (require) => { callback(null, require("./containers/Post").default) } ); function createRoutes() { return ( <Route path="/app/" component={App}> //    lazy-loading,       // <IndexRoute omponent={/**/}/> <IndexRoute getComponent={getPosts}/> <Route path='post/:id' getComponent={getPost}/> </Route> ) } export default createRoutes 

Common middleware for redux is connected as standard in the file koa_react_redux / src / storeCinfigurator.js

 import { createStore, combineReducers, compose, applyMiddleware } from 'redux' import promiseErrorLogger from './middlewares/promiseErrorLogger' createStore( combineReducers({ ...reducers }), initialState, compose( applyMiddleware( promiseErrorLogger, ) ) ) 

Client middleware in koa_react_redux / src / index.js:

 import promiseErrorLogger from './middlewares/promiseErrorLogger' import { configureStore} from './storeCinfigurator' configureStore(browserHistory, window.init, [promiseErrorLogger]); 

Server by analogy in the file koa_react_redux / app / controllers / reactAppController.js.

Asynchronous actions:

 import {GET_POSTS} from './actionsTypes' import superagentFactory from '../helpers/superagentFactory' const superagent = superagentFactory(); export function getPosts(){ return { type: GET_POSTS, payload: superagent .get('/getPosts/') .then(res => res.body) } } 

For asynchronous action reduser:

 import {GET_POSTS, PENDING, SUCCESS, ERROR} from '../actions/actionsTypes'; export default function(state = [], action = {}){ switch (action.type){ case GET_POSTS + SUCCESS: return action.payload; case GET_POSTS + PENDING: return state; case GET_POSTS + ERROR: return state; default: return state; } } 

Redux reducer connectors in the file koa_react_redux / src / reducers / index.js:

 import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux' import posts from './postsReducer' const rootReducer = { posts, routing: routerReducer }; export default rootReducer; 

We write the general configuration by analogy of config js in the folder koa_react_redux / config /, our own wrapper was made for isomorphic use.

Server configuration is written as:

 const config = { //  }; // for cut server-side config if (typeof cutCode === 'undefined') { Object.assign(config, { //   }); } module.exports = config; 

For SEO, our team uses the helmet library))) ( react-helmet )

It works like this:

 //     import Helmet from "react-helmet"; <div className="application"> <Helmet htmlAttributes={{"lang": "en", "amp": undefined}} // amp takes no value title="My Title" titleTemplate="MySite.com - %s" defaultTitle="My Default Title" base={{"target": "_blank", "href": "http://mysite.com/"}} meta={[ {"name": "description", "content": "Helmet application"}, {"property": "og:type", "content": "article"} ]} link={[ {"rel": "canonical", "href": "http://mysite.com/example"}, {"rel": "apple-touch-icon", "href": "http://mysite.com/img/apple-touch-icon-57x57.png"}, {"rel": "apple-touch-icon", "sizes": "72x72", "href": "http://mysite.com/img/apple-touch-icon-72x72.png"} ]} script={[ {"src": "http://include.com/pathtojs.js", "type": "text/javascript"}, {"type": "application/ld+json", innerHTML: `{ "@context": "http://schema.org" }`} ]} onChangeClientState={(newState) => console.log(newState)} /> ... </div> 

Data for server-rendering is written in a container, which is connected in routs:

 import {getPosts} from '../actions' class Posts extends Component { constructor(props) { super(props); } //         static fetchData(dispatch, uriParams, allProps = {}) { const promiseArr = [ dispatch(getPosts()), ]; //        return Promise.all(promiseArr); } render(){ return ( //  ); } } 

PS I advise you to post api and distribute scripts on separate projects in order to avoid incidents and reliability. I will be glad to hear your comments and comments.

Source: https://habr.com/ru/post/310302/


All Articles