Reactでチャットアプリケーションを作ったので、その際に調べたことをちょっとメモ。 ちなみに作ったのはこれだから、試してみるのもありでしょう。
どう作ったのかはこの記事に書いてあります。
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渡すんだろう・・・と思ってたけど、できる。
一番親で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>
を入れないと二重にコンポーネントが表示されてしまうから注意。
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
だそうです。
componentWillMount()に時間のかかる処理入れない方が良いかも
componentWillMount()に認証の処理をするためにfirebaseとの接続処理を書いていたんだけど、それがcomponentWillMount()の時に 終わらなく、期待したcomponentが表示されない場合がある。
axios使った場合もそうっぽいですね。 こういう場合は、componentDidMountに書くのもありなのかもしれない。
return内でifを使いたい
return内で条件によって返すcomponentを変えたい時がある。自分の場合は、認証済みか否かで変えたかった。そんな時は三項演算子なら使えるので、三項演算子を使ってスマートに書く。
render() { return( this.state.isAuthenticated ? ( this.props.children ) : ( <Redirect to={'/login'} /> ) ) }
即時関数で頑張る方法もあるみたいですね。
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のライブラリを使いました。
autofocusされない
inputタグの中に普通にautofucusと記述しても動作しないので、書き方を変える。
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に対するスタイルを適用することができる。