Posted at

【基礎】ReactでServer Side Rendering

More than 1 year has passed since last update.


概要

SSRの勉強をしたくて、Qiitaを漁ったら、

思いの外、他の方の記事がわかりづらかった(私の能力が低いだけです


Express + ReactでSSR

参考:React での ServerSideRendering 入門


準備

npm install express-generator -g

express myapp
cd myapp
npm install
npm install react react-dom node-jsx

npm start


サンプルコンポーネント

ただのReactコンポーネント。


components/sample.js

const React = require('react');

module.exports = class Sample extends React.Component {
render(){
return (
<div>
hoge
</div>
);
};
};



Viewをブラウザに返す

node-jsxを入れておくと、requireでJSXを解釈してくれるので

入れておいたほうがいい。

Reactコンポーネントを読み込んで、

ReactDomServer.renderToStringでHTML文字にする。

あとはそれをブラウザに渡す。


app.js

・・・

require('node-jsx').install({harmony: true});

const React = require('react');
const ReactDomServer = require('react-dom/server');

var Sample = require('./components/sample');

const createHtml = component => (`
<html>
${component}
</html>
`
);

・・・

//app.use('/', index);
app.use('/', (req, res, next) => {
const sampleComp = ReactDomServer.renderToString(React.createElement(Sample));
const view = createHtml(sampleComp);
res.status(200).send(view);
});

・・・



今表示されているものは静的なHTML

のはず。

hogeを表示させるだけだとよくわからないので、

Componentのstateに、変化と、その表示をさせてみよう。


components/sample.js

const React = require('react');

module.exports = class Sample extends React.Component {
constructor(props) {
super(props);

this.state = {
count: 0
};

this.click = this.click.bind(this);
}

render(){
return (
<div>
<input type="button" onClick={this.click} value="CountUP"></input>
<div>{this.state.count}</div>
</div>
);
};

click() {
this.setState({
count: this.state.count + 1
});
}
};


うん、ボタン押しても動かない。

noReact.gif


ブラウザでReactを動かす

あれじゃあどうやって仮想DOM使うんだ・・?

と調べると・・。

参考:hydrateでSSR後にブラウザでReactを動かす

マジカヨ。

結局、ブラウザ用にファイル用意するのね・・。

わしゃてっきり、webpackから解放されるものかと・・。


というわけでwebpack準備

npm install -D \

webpack webpack-cli \
babel-cli babel-preset-env babel-preset-react babel-loader

※"webpack": "^4.10.1"


webpack.config.js

const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'public'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'babel-preset-react',
'babel-preset-env'
]
}
}
}
]
}
};


肝は、express-generatorで作成したアプリなので、

デフォルトでpublicディレクトリが静的なファイルを置く場所と決まっていることだ。

なので、bundleファイルもそこに出力されるようにする。


表示の置換

参考を見る限り、

静的HTMLを、ReactのViewに後から置き換わるようにすれば良いみたい。

これは、ブラウザでReactを書いている人には馴染み深い

ReactDOM.render

とほぼ一緒だ。

renderではなく置換(という言葉であっているのか・・?)させる、

ReactDOM.hydrate

に変えてあげればいいみたい。


src/index.js(ブラウザ側Reactのエントリーポイント)

const React = require('react');

const ReactDOM = require('react-dom');
var Sample = require('./../components/sample');

ReactDOM.hydrate(
<Sample />,
document.getElementById('root')
);


bundleファイルを読み込みと、

DOMにレンダリング(置換)しないといけないので、最親のHTMLにコンテナを用意。


app.js

・・・

const createHtml = component => (`
<html>
<div id="root">
${component}</div>
<script src="index.js"></script>
</html>
`
);

・・・



bundleファイル作って、サーバ起動して・・

./node_modules/.bin/webpack

npm run start

動いた動いた。

addReact.gif