JavaScript素人が勉強の一環でVue.jsでアプリ作成していたものをReactでも書き起こしてみました。
Reactを書いてみて良かった点や、つまづきポイントをまとめていきたいと思います。
※アプリ本体はお粗末なもの(未作成ページあり、スタイル超適当)なのでご了承ください。
作成物の見た目
※両方CSSがバグり散らかしてますが、許してください。
DatePickerの仕様がVue.jsとReactで異なるのか見た目が異なっているが、それ以外はVue.jsで書いたコンポーネントをほぼそのままReactでも利用できました。
Vue.jsで作成したものがこちら
Reactで作成したものがこちら
Reactの良かった点
JSXの記述方法が直感的にわかりやすい
JSXについてはこちらをご参照ください。
https://ja.reactjs.org/docs/introducing-jsx.html
マークアップ言語を、Java等の言語に近いような感覚で書けるのでその点は描きやすいと感じました。
Vueと比較してJavaScriptで書いている感じなので、マークアップ言語を書いている感覚が個人的にないのが良いです。
<template>
<div class="tlArea">
<notify-area></notify-area>
<div class="calendar">
<DatePicker isDark="true" :formats="formats" v-model="date"
style="font-size: 18px; margin-right: 15px; float:left"/>
</div>
<activity-info style="padding-left: 5px;"></activity-info>
</div>
</template>
<script>
// @ is an alias to /src
import { DatePicker } from 'v-calendar';
import NotifyArea from './v-notification-area.vue'
import ActivityInfo from './v-activity-info.vue'
export default {
name: "TimeLineArea",
components: {
DatePicker,
NotifyArea,
ActivityInfo
}
}
</script>
<style>
//省略
</style>
import React, {useState}from 'react';
import NotificationArea from './NotificationArea';
import ActivityInfo from './ActivityInfo';
import './timeLineArea.css'
import DatePicker from 'react-datepicker';
import "react-datepicker/dist/react-datepicker.css"
const TimeLineArea = () => {
const initialDate = new Date()
const [startDate, setStartDate] = useState(initialDate)
const handleChange = (date) => {
setStartDate(date)
}
return (
<div className="tlArea">
<NotificationArea/>
<div className="calendar">
<DatePicker selected={startDate} onChange={handleChange}/>
</div>
<ActivityInfo style={{ 'padding-left': '5px' }}/>
</div>
);
}
export default TimeLineArea;
Reactつまづきポイント
1. レンダリングの無限ループ
例えば以下のコード。
<ul className="tabMenu">
{tabs.map((item) => {
return <li className="tabItem" key={item.id}
onClick={dispChange(item.id)}>{item.name}</li>;
})}
</ul>
タブメニューを表示するコードですが、以下のような無限ループに陥ります。
Too many re-renders. React limits the number of renders to prevent an infinite loop.
これはReactのレンダリングの性質上によるものだそう。
あまり詳しくないので理解しきれていないが、VueとReactでレンダリングタイミングの違いがある模様。
Reactではpropsやstateが更新されると、変更されたコンポーネントと子コンポーネントが再レンダリングされるそうです。
なので上記例ではonClickイベントでstateの更新を行い、コンポーネントが再レンダリングされた際に以下のようなループが起こっていそうでした。
onClick実行→state更新→再レンダリング(onClick再実行)→state更新→再レンダリング・・・
参考:https://qiita.com/doz13189/items/e58868d4ec037f572df2
解決方法としては、関数化が良いそうなので、アロー関数を使いました。
<ul className="tabMenu">
{tabs.map((item) => {
return <li className="tabItem" key={item.id}
onClick={()=>dispChange(item.id)}>{item.name}</li>;
})}
</ul>
2. 条件分岐やループ文が書きにくい
JSXがjavaScriptのように書けると言いましたが、ifやfor文が場合によって使えません。
タブ切り替えの際に条件分岐しようと以下のように書くとエラーになります。
return (
<div className="tab">
<ul className="tabMenu">
{tabs.map((item) => {
return <li className="tabItem" key={item.id}
onClick={()=>dispChange(item.id)}>{item.name}</li>;})}
</ul>
//タブ切り替え
if (active === 1) {
return <TimeLineArea/>;
} else if (active === 2) {
return <ActivityHukei/>;
} else //省略
</div >
);
解決法は、以下の方法がありました。
・条件分岐したいところをJSX外部の関数として書く←今回はこれで解決
・即時関数の中に入れる
参考:https://www.yoheim.net/blog.php?q=20180409
const dispContent = (active) => {
if (active === 1) {
return <TimeLineArea/>;
} else if (active === 2) {
return <ActivityHukei/>;
} else //省略
}
return (
<div className="tab">
<ul className="tabMenu">
{tabs.map((item) => {
return <li className="tabItem" key={item.id}
onClick={()=>dispChange(item.id)}>{item.name}</li>;})}
</ul>
//タブ切り替え
{dispContent(activeTab)} //JSXの外でメソッド化して呼び出し
</div >
);
ちなみにVue.jsではタブ切り替えの部分の条件分岐を以下のように書けます。
なんとなくVue.jsの方がそのまま素直に書ける印象です。
<time-line-area v-show="activetab===1"/>
<v-active-hukei v-show="activetab===2"/>
ループ文に関しても同様で、ループ処理する際は以下の対応が必要でした。
・ループ内容をJSX外部の関数として書く
・for文を即時関数に入れる
・map()メソッドを使う
...etc
参考:https://www.i-ryo.com/entry/2020/03/04/082411#JSXでfor文成功例pushを使う
書き比べた感想
ReactはJavaScriptをしっかり理解していないと、つまづくところが多い印象を受けた反面、
Vue.jsではやりたいことを素直に書けそうな感覚でした。
そういった意味ではVue.jsの学習コストが低いと言われている理由が何となくわかる気がしました。
まだ簡単なことしか書いていないので、このような感想を抱いているだけかもしれないので
学習を続けてReactの恩恵を感じられるように頑張ります。