はじめに
社内の勉強会用に作りました。
できるだけシンプルなのもを作りたいと思い、ブログにしてみました。
やりたい事
- React + Redux + React-Routerで作りたい。
- ブログっぽいものを作りたい。
- Markdownで記事は書きたい。
- シンタックスハイライトしたい。
- 管理画面は作りたくない。
- 記事を読んでいる量を表示したい。(Pocketみたいなやつ)
ソースやサイトはこちらです(まだアップデートする予定)
github : https://github.com/yutasuzuki/React-redux-blog/
各ライブラリの特徴を理解する
公式サイトやREADMEを軽く目を通したり、チュートリアルをやったりしています。
その上で以下のサイトや記事、書籍がとても参考になりました。
- [React] 入門 React ―コンポーネントベースのWebフロントエンド開発
- [Redux] いまから始めるWebフロントエンド開発
- [React-Redux] たぶんこれが一番分かりやすいと思います React + Redux のフロー図解
- [redux-saga] redux-sagaで非同期処理と戦う
ブログを作るに当たって考えた事
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>
)
}
}
まとめ
社内勉強用に作ってきたもので、本当にこんな設計でいいのかちょっと不安ですが、作っていて楽しかったです!
また理解を深める意味でも、別のものも作って見たいと思います。