ReactとFirebaseで作ったチャットアプリ開発メモ

Reactでチャットアプリケーションを作ったので、その際に調べたことをちょっとメモ。 ちなみに作ったのはこれだから、試してみるのもありでしょう。

Twichat

どう作ったのかはこの記事に書いてあります。

hydro-pump.hatenablog.com

componentWillMount

componentWillMount()中にsetStateしてもcomponentWillMount()の中ではそのセットしたstateを参照できない。

this.setState({ 
  description: this.props.room.description,
  key: this.props.room.key,
});

componentWillMount()の中でこんなことをしてconsole.log()でstateが変更されたかなあと確かめても、何も変更されていない。setStateは即座に反映されるわけではない。

react-router

react-routerでどうやってprops渡すんだろう・・・と思ってたけど、できる。

ngzm.hateblo.jp

一番親でstateを管理したいっていう時にしか使わないだろうけど、まあそんなこともできる。

デフォルトのroute

pathを指定しなければ、指定したパス以外のコンポーネントを指定することが可能。

  <Route component={Login} />

画面遷移

React Router(v3)では使えたbrowserHistory.push()はv4では使えない。v4では、withRouterを使うことでクリックしないでも画面遷移をすることができる。

import React from "react";
import { withRouter } from "react-router-dom";

class MyComponent extends React.Component {
  ...
  myFunction() {
    this.props.history.push("/some/Path");
  }
  ...
}
export default withRouter(MyComponent);

画面遷移時に他のコンポーネントにpropsを渡したい

上記で出たようにthis.props.history.push()を使うことでreact-routerで指定した別のコンポーネントに遷移することはできるけど、遷移時に何かしらの値を渡したい時もあるでしょう。

その場合は、以下のようにすれば渡すことが可能です。

 this.props.history.push({
    pathname: "/rooms",
    state: { profile: this.state }
}); 

この例で言えば、/roomsで表示されるコンポーネントにstateを渡しています。 roomsのコンポーネントでは、props.state.profileで受け取ることが可能です。constructor内でconsole.logで表示してみると分かります。

Switch

Switchを使うことで、ルーティングを定義可能。

  <Router>
    <div>
      <Switch>
        <Route exact path="/login" component={Login} />
        <Route exact path="/rooms" component={Rooms} />
        <Route component={Login} /> 
      </Switch>
    </div>
  </Router>

pathを指定しなければ、/時に表示するコンポーネントを指定することができる。

認証

react-router

react-routerを使って認証済みの場合と認証済みでない場合のページを分けたい!という時には、認証用のcomponentを作ってそれをreact-routerのコンポーネントに追加すれば良い。

      <Router>
        <div>
          <Switch>
            <Route exact path="/login" component={Login} />
            <Auth>
              <Switch>
                <Route exact path="/rooms" 
                  render={props => <Rooms users={this.state.users} />}
                />
                <Route exact path="/rooms/:roomId"
                  render={props => <Room  {...props} hoge={hoge => this.handleHoge(hoge)} />}
                />
                <Route component={Rooms} />
              </Switch>
            </Auth>
            <Route component={Login} />
          </Switch>
        </div>
      </Router>

ちなみに、<Auth></Auth>の中でも<Switch>を入れないと二重にコンポーネントが表示されてしまうから注意。

qiita.com

firebase

認証してない場合トップページに返すおような実装をしようと思い、firebase.auth().currentUserのuserの有無で判断しようと思ったけどそうはいかない。 こっちの書き方をすると良いみたい。

firebase.auth().onAuthStateChanged(function(user) { if (user) { // User is signed in. } else { // No user is signed in. } });

during initialization firebase.auth().currentUser is null so you should use

だそうです。

forum.ionicframework.com

componentWillMount()に時間のかかる処理入れない方が良いかも

componentWillMount()に認証の処理をするためにfirebaseとの接続処理を書いていたんだけど、それがcomponentWillMount()の時に 終わらなく、期待したcomponentが表示されない場合がある。

stackoverflow.com

axios使った場合もそうっぽいですね。 こういう場合は、componentDidMountに書くのもありなのかもしれない。

qiita.com

return内でifを使いたい

return内で条件によって返すcomponentを変えたい時がある。自分の場合は、認証済みか否かで変えたかった。そんな時は三項演算子なら使えるので、三項演算子を使ってスマートに書く。

  render() {
    return(
        this.state.isAuthenticated ? (
          this.props.children
        ) : (
          <Redirect to={'/login'} />
        )
    )
  }

即時関数で頑張る方法もあるみたいですね。

qiita.com

styled-componens

<div className="hoge">

のように指定してcssを適用させるコードをよくみることもあると思うけど、styled-componentsを使えば、こういった書き方はしなくて良くなります。

import styled from 'styled-components';

const Wrapper = styled.div`
  padding: 10px;
  margin: 0 auto;

`

render() {
  return(
    <Wrapper>
     ・・・
    </Wrapper>
  )
}

のように書くことができる。命名に悩むことはあるけど、普通のcssのように書くことができるので使いやすいです。

material-ui

一部分マテリアルuiのライブラリを使いました。

material-ui.com

autofocusされない

inputタグの中に普通にautofucusと記述しても動作しないので、書き方を変える。

portaltan.hatenablog.com

styled-componentsでメディアクエリ

styled-componentsでメディアクエリを使うこともできる。

import { css } from 'styled-components';

export const media = {
  desktop: (...args) => css`
    @media (min-width: 1280px) {
      ${css(...args)}
    }
  `,
  desktopMini: (...args) => css`
    @media (min-width: 960px) and (max-width: 1280px) {
      ${css(...args)}
    }
  `,
  tablet: (...args) => css`
    @media (min-width: 600px) and (max-width: 960px) {
      ${css(...args)}
    }
  `,
  spLandscape: (...args) => css`
    @media (min-width: 480px) and (max-width: 600px) {
      ${css(...args)}
    }
  `,
  spPortrait: (...args) => css`
    @media (min-width: 0px) and (max-width: 480px){
      ${css(...args)}
    }
  `,
}

styled-componentsからcssを取ってきて、普通にメディアクエリを記述する。それぞれの幅に対する名前は自由に決めることができる。 これを他のコンポーネントで使いたい時には、他のコンポーネントと同じようにimportして以下のように書く。

import { media } from '../containers/style-utils';

const LeftContent = styled.div`
  width: 80%;
  display: table-cell;
  float: left;
  ${media.desktop`padding-bottom: 7%;`}
  ${media.desktopMini`padding-bottom: 7%;`}
  ${media.tablet`padding-bottom: 15%;`}
  ${media.spLandscape`padding-bottom: 15%;`}
}
`;

media.〇〇という風に書けば、それに対応したwidthに対するスタイルを適用することができる。