JavaScript
React
redux
es2015

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>
    )
  }
}

まとめ

社内勉強用に作ってきたもので、本当にこんな設計でいいのかちょっと不安ですが、作っていて楽しかったです!
また理解を深める意味でも、別のものも作って見たいと思います。