夢のかけら

Goエンジニアの技術ブログ

【React】Pessimistic UpdateとOptimistic Updateの違い

f:id:lampler:20210602120844p:plain

Pessimistic Updateとは?

Pessimistic Update(悲観的な更新)とは非同期通信が成功するか分からないので、成功or失敗するまで待ってから手元のDOMを更新する方法である。非同期通信の結果を待つ必要があるので非常に遅い。

// post = {id: 1, name: 'hoge'}のようなオブジェクト 

handleDelete = async post => {
  // posts/3のようなurlへdeleteメソッドでアクセス
  await axios.delete(`${apiEndpoint}/${post.id}`);
  // 非同期通信が終わってからstateを更新する
  // 引数で受け取ったpostを除外する新しい配列postsを作成
  const posts = this.state.posts.filter(p => p.id !== post.id);
  this.setState({ posts }); //このタイミングでviewが再レンダリングされる
}

Optimistic Updateとは?

Optimistic Update(楽観的な更新)とは非同期通信が成功することを前提としてローカルのリソースを先に更新することである。つまり、サーバー上のリソースの変更が成功したかどうかに関わらず、先にstateを更新してDOMを更新するのである。awaitpromiseオブジェクトを待つ必要がないので、スピードが上がりUXが向上される。

handleDelete = async post => {
  const originalPosts = this.state.posts;
  // 引数で受け取ったpostを除外する新しい配列postsを作成
  const posts = originalPosts.filter(p => p.id !== post.id);
  this.setState({ posts }); //このタイミングでviewが再レンダリングされる

  try {
    await axios.delete(`${apiEndpoint}/${post.id}`);
  } catch (ex){
    // もし失敗するとviewが再レンダリングされて元に戻る
    alert('投稿の削除に失敗しました')
    this.setState({ posts: originalPosts })
  }
}

【React】コンポーネントを再利用できるようにするリファクタリング

f:id:lampler:20210530073141p:plain 例えばこのような2つのコンポーネントがある。これらのコンポーネントの問題点は何だろうか?

親コンポーネント

state = {
  users: [{ id: 1, name: 'rin' }, { id: 2, name: 'sin' }]
}

render () {
  <div className="container">
    <ListGroup items={this.state.users} />
  <div>
}

子コンポーネント

const ListGroup = ({items}) => {
  return (
    <ul className="list-group">
      {items.map(item =>(
        <li key={item.id} className="list-group-item">
          {item.name}
        </li>
      ))}
    </ul>
  )
}

答え:再利用できないこと

コンポーネントは再利用してナンボである。今回の例において何が良くないかと言うと、コンポーネントが親からもらうオブジェクトの属性を決め打ちしてしまっていると言うである。つまり<li key={item.id} className="list-group-item"> {item.name}という箇所である。itemのプロパティidではなく_idだったら、nameではなくuserNameだったら、再利用できないよね?という話である。上のコードは子と親が密結合なのである。

リファクタリング

親から子にpropsとしてデータを渡す時に、オブジェクトの属性名も一緒に渡す。 子ではブラケット記法([])をつかって動的に属性を変更する。こうすることによって、子コンポーネントの再利用が容易になる。

親コンポーネント

state = {
  users: [{ id: 1, name: 'rin' }, { id: 2, name: 'sin' }],
  languages: [
    { number: 1, langName: 'JavaScript'}, 
    { number: 2, langName: 'typeScript' }
  ]
}

render () {
  <div className="container">
    <ListGroup items={this.state.users} textProperty="name" valueProperty="id"/>
    <ListGroup items={this.state.languages}  textProperty="langName" valueProperty="number"/>
  <div>
}

子コンポーネント

const ListGroup = ({items, textProperty, valueProperty}) => {
  return (
    <ul className="list-group">
      {items.map(item =>(
        <li key={item[valueProperty]} className="list-group-item">
          {item[textProperty]}
        </li>
      ))}
    </ul>
  )
}

ListGroupコンポーネントを再利用できるようになりました!

defaultPropsを設定してさらに見やすく

親から子へpropsでデータを渡す数は少ない方がいい。純粋に見やすくなる。  <ListGroup items={this.state.users} textProperty="name" valueProperty="id"/> ListGroupに渡すオブジェクトのほとんどがname,idプロパティを持っているとしよう。 こんな時はdefalut propsとして親の方で明示しなくても、子がデータを受け取ることができる。

親コンポーネント

state = {
  users: [{ id: 1, name: 'rin' }, { id: 2, name: 'sin' }],
  languages: [{ number: 1, langName: 'JavaScript'}, { number: 2, langName: 'typeScript' }]
}

render () {
  <div className="container">
    <ListGroup items={this.state.users} /> // 属性を渡さない
    <ListGroup items={this.state.languages}  textProperty="langName" valueProperty="number"/>
  <div>
}

子コンポーネント

const ListGroup = ({items, textProperty, valueProperty}) => {
  return (
    <ul className="list-group">
      {items.map(item =>(
        <li key={item[valueProperty]} className="list-group-item">
          {item[textProperty]}
        </li>
      ))}
    </ul>
  )
}

// 追加
// デフォルトでtextProperty = 'name'のように親からデータを受け取る
ListGroup.defaultProps = {
  textProperty: 'name',
  valueProperty: 'id'
}

【React】styled-componentsを使ってJavaScriptでCSSを書く

f:id:lampler:20210529094111p:plain

純粋なcssはエラーを吐かなかったり、クラス名を重複しないようにしたり、cssのためだけにクラス名をつけたり、と言ったデメリットが存在する。それを解決するstyled-componentsを使っていく。

インストール

$ npm install styled-components エディタにstyled-components用のプラグインを入れておくとよい。 プラグインがないとCSSに色がつかなくて見にくい。

基本

変数名 = styled.要素名という文法で書く。 その変数はコンポーネントとして利用できる。コンポーネントは要素名で指定した要素になる。今回ならdivが生成される。

import styled from 'styled-components'

const Main = () => {

 const Box = styled.div`
  background-color: aqua;
  width: 50%;
  height: 200px;
 `

  return <Box />
}

export default Main

f:id:lampler:20210529094248p:plain

sytled-componentsによってクラス名がつけられている

f:id:lampler:20210529094509p:plain

複数コンポーネント

import styled from 'styled-components'

const Main = () => {

 const Box = styled.div`
  background-color: aqua;
  width: 50%;
  height: 200px;
 `
 const BoxName = styled.h1`
 background-color: lime;
 `

  return (
    <Box>
      <BoxName> Hello React!</BoxName>
    </Box>
  )
}

export default Main

f:id:lampler:20210529094307p:plain

引数を渡す

引数はコンポーネント作成時にpropsとして受け取ることができる。${}で変数展開をする。 直接${props.bgColor}のように記述してもダメで、アロー関数の引数をpropsとする必要がある。`

import styled from 'styled-components'

const Main = () => {

 const Box = styled.div`
  background-color: aqua;
  width: 50%;
  height: 200px;
 `
 const BoxName = styled.h1`
 background-color: ${props => props.bgColor};
 color: ${props => props.color ? props.color : 'black'};
`

  return (
    <Box>
      <BoxName bgColor="red" color="white"> Hello React!</BoxName>
      <BoxName bgColor="lime"> Hello JavaScript!</BoxName>
    </Box>
  )
}

export default Main

f:id:lampler:20210529094323p:plain

ベースコンポーネントを継承する

例えば文字は基本的には赤色だが、場合によっては黒にしたい、フォントを太くしたい、といった時に役に立つ。HTML要素もベースとなるコンポーネントを継承する。例えばベースコンポーネントdivなら継承して作成されたコンポーネントdivである。継承のやり方はstyled(baseComponentName)と書くだけ。

import styled from 'styled-components'

const Main = () => {

 const Box = styled.div`
  background-color: aqua;
  width: 50%;
  height: 200px;
 `
  const BoxName = styled.h1`
    color: orangered;
    text-align: center;
  `

// BoxNameコンポーネントを継承。h1タグとcssを継承する。
// cssは上書きすることができる。
  const BoxNameWhite = styled(BoxName)`
    color: black;
    font-weight: bold;
    background-color: lime;
  `

  return (
    <Box>
      <BoxName> Hello React!</BoxName>
      <BoxNameWhite> Hello JavaScript!</BoxNameWhite>
    </Box>
  )
}

export default Main

f:id:lampler:20210529094354p:plain

CSSをネストをさせる

ネストもSCSSのように非常に簡単にできる。

import styled from 'styled-components'

const Main = () => {

 const Box = styled.div`
   h2 {
     color: green;
     :hover {
       text-decoration: underline;
     }
   }
  background-color: aqua;
  width: 50%;
  height: 200px;
 `
  const BoxName = styled.h1`
    color: orangered;
    text-align: center;
  `

  const BoxNameWhite = styled(BoxName)`
    color: black;
    font-weight: bold;
    background-color: lime;
  `

  return (
    <Box>
      <BoxName> Hello React!</BoxName>
      <BoxNameWhite> Hello JavaScript!</BoxNameWhite>
      <h2>Hello TypeScript!</h2>
    </Box>
  )
}

export default Main

f:id:lampler:20210529094418p:plain おしまい!Vueの楽しいけどReactも楽しい!

【JavaScript】アロー関数の引数で分割代入をする

f:id:lampler:20210522070058p:plain Reactでよく使われるアロー関数の引数で分割代入を行うパターンを解説する。

まずは分割代入の復習から

分割代入はオブジェクトのキーと値をオブジェクトの外に出して変数として使えるようにすること。 英語名はObject destructuringで、その名の通りオブジェクトを破壊して値を取り出す、といったイメージである。オブジェクトに存在しないキーを指定するとundefinedが格納される。

const person = { name: 'lamp', age: 22, isMan: true }
console.log(name, age)
// => Uncaught ReferenceError: Cannot access 'name' before initialization"

const { name, age, adress } = person
console.log(name, age, adress) // => "lamp", 22, undefined

別名をつけて分割代入

キー : 新しい名前という文法で書く。

const person = { name: 'lamp', age: 22 } 
const { name: userName,  age: userAge } = person

console.log(userName, userAge) // => "lamp", 22
console.log(name, age) // => Uncaught ReferenceError: age is not defined"

アロー関数の引数で分割代入

呼び出すときは引数で代入しているプロパティをもったオブジェクトを渡す。

const person = { 
  name: 'lamp', 
  age: 22, 
  isMan: true, 
  money: 36, 
  haveChild: true
}

const fn = ({ name, age, isMan, ...rest }) => {
  console.log(name, age, isMan);
  console.log(rest);
}

fn(person) 
// => "lamp", 22, true
// => { haveChild: true, money: 36 }

見ての通り、可変調引数restには配列ではなくオブジェクトが格納されている。

【React】子コンポーネントから親コンポーネントへデータを渡す

f:id:lampler:20210519200542p:plain

子が好きなタイミングで親のメソッド実行する

Vueと同じように子コンポーネントの好きなタイミングで親コンポーネントに作成したカスタムイベントを発火させることができる。その時の引数としてデータを受け取る、という流れである。

注意点として、親コンポーネントではカスタムイベント={メソッド名}として関数への参照を渡す。 子から送られてきたデータは引数としてメソッド内で受け取ることができる。

// 親コンポーネント

handleMyEvent = (value) => {
  console.log(value) // 3
}

<Counter myEvent={this.handleMyEvent} />
 


// 子コンポーネント
<button onClick={() => this.props.myEvent(3)} >myEventを発火</button>

データフローは単一方向

データフローは単一方向、つまり「親から子へ」という一方通行である。 親のデータを子から変更することはできない。子のデータは親からもらうので、もちろん子は親のデータに依存している。 具体例で説明しよう。

親が貧乏になると一緒に子どもも一緒に貧乏になる。一方で、子どもが親からもらったお小遣いを使い果たしても親は貧乏にならない。子どもは親のお金を勝手に使うことはできない。じゃあどうするか?子どもは親に「お金が欲しい。いくら欲しい」ということを伝えるのだ。親は事前にお金がなくなったら言いなさい、と子どもにカスタムイベントを渡している。あとはそれを、子どもが引数つき(なくてもよい)で、好きなタイミングで実行するのだ。親は我が子の助けが来たらお金をあげようとずっと待ち構えている(イベントを購読)。親はそのイベントが起きるとまたお金をあげる、つまり任意の関数を実行するのである。

controlled component

controlled componentとは自分でstateを持たないコンポーネントのことである。 propsで親から全てのデータを受け取り、データを変更したい時は親からもらったイベントを発火させる。 そして親のメソッド内でデータを変更する。 つまり、その名の通り完全に親に依存するコンポーネントである。

【Git 】git switchでリモートブランチをローカルに持ってくる

f:id:lampler:20210517060943p:plain

リモートブランチと同じ名前のブランチをローカルに持ってくる

新しいコマンドのgit switchが便利! $ git switch remote_branch_name

もしうまくいかない場合は$ git fetchを実行してローカルのリモート追跡ブランチを更新してください。

リモートブランチと違う名前のブランチを作成する必要がある時

$ git checkout -b branch_name origin/branch_name branch_nameは自分で命名できる。

【Rails】formタグ内のbuttonを押すと意図せずにPostリクエストが送信されてしまう時の対処法

f:id:lampler:20210516081631p:plain

Railsで作るフォームの中で、Reactを使う必要があった。 Reactのコンポーネントで作ったボタン(<button>)を押すと、なぜかpostリクエストが飛んでしまう。

原因

<form>の<button><button type="submit">がデフォルトになっているようだ。

対処法

type="button"を追加する。

<button type="button">Increment</button>