Edited at

React + Redux + React-Routerでブログを作りました。

More than 1 year has passed since last update.


はじめに

社内の勉強会用に作りました。

できるだけシンプルなのもを作りたいと思い、ブログにしてみました。


やりたい事


  • React + Redux + React-Routerで作りたい。

  • ブログっぽいものを作りたい。

  • Markdownで記事は書きたい。

  • シンタックスハイライトしたい。

  • 管理画面は作りたくない。

  • 記事を読んでいる量を表示したい。(Pocketみたいなやつ)


ソースやサイトはこちらです(まだアップデートする予定)

github : https://github.com/yutasuzuki/React-redux-blog/


各ライブラリの特徴を理解する

公式サイトやREADMEを軽く目を通したり、チュートリアルをやったりしています。

その上で以下のサイトや記事、書籍がとても参考になりました。


ブログを作るに当たって考えた事


Q. React Reduxでどこから作っていけば良いのか?

A. どこからでも良いと思うんですが、自分はcomponentから作ると作りやすいと感じました。

component → container → action → reducerって感じで作っていたと思います。


Q. Markdownをどう管理するか?

A. MarkdownのファイルをJSONにし、fetch APIで呼ぶことにしました。

一旦、次のようなコードでJSONにしています。

'use strict'

const fs = require('fs')
const path = require('path')
const util = require('util')
const md_dir = path.join(__dirname, 'md')

fs.readdir(md_dir, (err, files) =>{
let items = []
let settings = {
id: [],
titles: [],
promises: []
}
files.forEach((file) => {
const fp = path.join(md_dir, file)
const st = fs.statSync(fp)
const id = st.birthtime.getTime()
const title = file.replace( /.md/g , '' )
settings.id.push(id)
settings.titles.push(title)
settings.promises.push(readMarkdown(fp))
})

Promise.all(settings.promises).then((texts) => {
texts.forEach((text, index) => {
items.push({
id: settings.id[index] + index,
title: settings.titles[index],
text: text
})
})
fs.writeFile(`${__dirname}/data.json`, JSON.stringify(items))
})
})

const readMarkdown = (fp) => {
return new Promise((resolve, reject) => {
fs.readFile(fp, {
encoding: 'utf-8'
}, (err, text) => {
resolve(text)
})
})
}


Q. シンタックスハイライトをどこで付けるのか?

A. 今回あまり良い方法が思いつかなかったので、mapStateToProps内で力技で付けています。


使用ライブラリ


  • showdown

  • highlight.js

const preTags = htmlText.match(/<pre>[\s\S]*?<\/pre>/g)

preTags.forEach((preTag) => {
const pre = preTag.match(/<pre><code[\s\S]*?\">/i)
const code = pre.input.replace(pre[0], '').replace(/<\/code><\/pre>/, '')
const tag = hljs.highlightAuto(sanitize.decode(code))
htmlText = htmlText.replace(preTag, `${pre[0]}${tag.value}</code></pre>`)
});

他に良い方法があれば教えて頂きたいです。


Q. 記事を読んでいる量をどう表示するか?

A. 記事を読んでいる量をページの高さから取得しています。

始めはReduxのstateで管理していました。しかし、毎スクロールにstateが更新されるのでレンダリングの負荷が高くなってしまいました。

現在は単体で動くように管理下から外しています。(特にstate管理する必要もなかったので。)

なのでreadingScrollerとして単体のcomponentになっています。


import React, { Component } from 'react'

class ReadingScroller extends Component {
constructor(props) {
super(props)
this.state = {
scrollY: this.props.scrollY
}
this.event = {
readingScrollHandler: () => {
this.getRatioScroll()
}
}
}

componentDidMount() {
window.addEventListener('scroll', this.event.readingScrollHandler)
window.addEventListener('resize', this.event.readingScrollHandler)
this.getRatioScroll()
}

componentWillUnmount () {
window.removeEventListener('scroll', this.event.readingScrollHandler)
window.removeEventListener('resize', this.event.readingScrollHandler)
}

getRatioScroll() {
const windowHeight = window.innerHeight
const bodyHeight = document.body.clientHeight
const scrollY = window.scrollY / ( bodyHeight - windowHeight ) * 100
this.setState({ scrollY: scrollY })
}

render(){
const scroller = {
position: 'fixed',
top: 0,
left: 0,
right: 0,
width: `${this.state.scrollY}%`,
height: '2px',
backgroundColor: '#00a0e8',
WebkitTransition: '.1s ease',
transition: '.1s ease',
zIndex: 999,
opacity: .9,
}
return (
<div style={scroller}></div>
)
}
}


まとめ

社内勉強用に作ってきたもので、本当にこんな設計でいいのかちょっと不安ですが、作っていて楽しかったです!

また理解を深める意味でも、別のものも作って見たいと思います。