LoginSignup
30
26

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-02-25

はじめに

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

やりたい事

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

まとめ

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

30
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
26