Completing React

Now we finally get to build our React-application into something that can be run and will actually do something! Here we make the assumption that you are going to build a medium- to large-sized application and show how to do these things in a more modular way, but for smaller applications some of these parts can be merged with the IndexView and some can be left out completely.

Initialize

We begin by installing new dependencies called React Router and ReactDOM

yarn add react-router-dom react-dom

which adds the capability to define which URL equals which view and to render our React application to the DOM, respectively.

And of course the types for them

yarn add -D @types/react-router-dom @types/react-dom

AppView

We begin by writing an AppView.ts file into the src/modules-folder

import * as React from 'react';
import { Route, Switch, RouteComponentProps } from 'react-router-dom';
import IndexContainer from './index/IndexContainer';
import PageNotFound from '../components/PageNotFound';

export type IAppViewProps = RouteComponentProps<undefined>;

const AppView: React.StatelessComponent<IAppViewProps> = () => (
    <div className="app-base">
        <Switch>
            <Route path="/" exact component={IndexContainer} />
            <Route component={PageNotFound} />
        </Switch>
    </div>
);

export default AppView;

which is a fairly simple view, except for the <Switch>-clause and RouteComponentProps.

All views that go through React Router get some extra properties, which are included in RouteComponentProps<Params, where Params can be used to define possible parameters for the URL.

The <Switch> element is used to render a single view out of all Routes inside the <Switch>. Routes have three main properties you should remember:

  1. path which indicates what URL the view matches to (it can be used to define parameters as well)
  2. exact which indicates that the view should only match if the URL matches path exactly
  3. component which defines the actual view to render

AppContainer

Next we create a very simple container for AppView called AppContainer, which is situated in the same src/modules-folder

import { connect } from 'react-redux';
import AppView, { IAppViewProps } from './AppView'; //tslint:disable-line:no-unused-variable

export default connect<{}, undefined, IAppViewProps>(() => ({}))(AppView);

which we use just to wrap AppView so that it can be used in routes.

IndexView

For IndexView we also need to add RouteComponentProps, so just add the following

import { RouteComponentProps } from 'react-router-dom';
// ...
export type IIndexProps = IIndexState & IIndexDispatch & RouteComponentProps<undefined>;

index

Finally we create a file index.ts inside src

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Route } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import { AppContainer as HotContainer } from 'react-hot-loader';
import createHistory from 'history/createBrowserHistory';
import configureStore from './redux/store';
import AppContainer from './modules/AppContainer';

const history = createHistory();

const render = (container: React.ComponentClass) => ReactDOM.render(
    <HotContainer>
        <Provider store={configureStore(history)}>
            <ConnectedRouter history={history}>
                <Route component={container} />
            </ConnectedRouter>
        </Provider>
    </HotContainer>,
    document.getElementById('app'),
);

render(AppContainer);

if ((module as any).hot) {
    (module as any).hot.accept('./modules/AppContainer', () => render(AppContainer));
}

which is the entry file to our application that ties everything together.


On the 14. line

import * as ReactDOM from 'react-dom';

const render = (container: React.ComponentClass) => ReactDOM.render(
    // ...,
    document.getElementById('app'),
))

we render our React-application to the DOM inside a div with the id app (we'll come back to this).


On the 15. line

import { AppContainer as HotContainer } from 'react-hot-loader';
// ...
    <HotContainer>
        // ...
    </HotContainer>,

we wrap our application into a container to enable Hot Module Replacement (more about it later in this section).


On the 16. line

import * as React from 'react';
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import configureStore from './redux/store';
const history = createHistory();
// ...
        <Provider store={configureStore(history)}>
            // ...
        </Provider>

which wraps our React application with Redux using Provider from react-redux, which takes a single parameter store, for which we provide our store as we defined it in Redux. history/createBrowserHistory is used to create a wrapper around the browser history we can use.


On the 17. line

import * as React from 'react';
import { Route } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import AppContainer from './modules/AppContainer';
// ...
        <ConnectedRouter history={history}>
            <Route component={AppContainer} />
        </ConnectedRouter>
// ...

we keep the UI in sync with the URL using a ConnectedRouter from react-router-redux, which takes as argument a history, where we give history we created previously. Here we define a single Route which renders AppContainer for all URL routes.


Finally at the end

if ((module as any).hot) {
    (module as any).hot.accept('./modules/AppContainer', () => render(AppContainer));
}

we do a little configuration to allow our container to be loaded by the Hot Module Replacement-system.

Index.html

Finally we write an index.html in our root-folder

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>TS-React boilerplate</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="/bundle.js"></script>
    </body>
</html>

which is just a very simple HTML-file, which imports our (soon-to-be-bundled) JavaScript from the current folder /bundle.js and contains a div with the id app so our index.ts works.

results matching ""

    No results matching ""