夢のかけら

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

VSCodeでGoを書いてgit pushするとインデントがおかしくなる時の対処法

f:id:lampler:20210704100053p:plain 次のお仕事でGolangを使うのでGoを勉強中です。しかし困ったことがありました。

起こったこと

  1. VSCodeでGoを書く
  2. 保存してgit pushする
  3. Git Hub上でコードをみる
  4. 何じゃこのアホみたいなインデント!!

f:id:lampler:20210704094306p:plain

原因

VSCode拡張機能、Goが保存時にインデントをスペースからタブへ変更していた。

そのタブはVSCode上の見た目では分からない。(Tabは→、Spaceは・で見ることはできるけど)

Githubではタブは8スペースで表示される。

対策

その1 Goの拡張機能をOffにする

極めて微妙。せっかくの拡張機能がなくなってしまう。 ↓の記事にgo.formatOnSavefalseにすればいけるよ、って書いてあるけどsetting.jsonに書いても動かなかった。

Indentation is replaced with tabs from spaces on save regardless of editor settings when this extension is enabled. · Issue #1930 · microsoft/vscode-go

でも普通に保存時のコード整形は欲しい。

その2 諦める

Goではインデントにタブ(4スペース前提)を使うことが推奨されている。だから強引にスペースにするのがそもそも間違っていた。 素直にフォーマッターに従うべきだ。

その3 Git HubのTabスペースを変更する

クエリパラメーターで?ts=2とか?ts=4とすると見た目を変更できる。だが毎回こんなことできるわけがない。 Chrome拡張機能を見つけた。これを使うか〜 Githubのタブが8文字で見にくいのでextensionで解決 - Qiita

【React】Hooksを使って関数コンポーネントに状態を持たせる

f:id:lampler:20210613072539p:plain

昔の話

昔は関数コンポーネントとクラスコンポーネントは明確に違った。

関数コンポーネント

  • 状態を持たない
  • ライフサイクルメソッド(componentDidMountなど)使えない

クラスコンポーネント

  • 状態を持つ
  • ライフサイクルメソッドが使える

このような違いあった。しかしHooksが導入されてから、関数コンポーネントでもクラスコンポーネントと同じことができるようになった。

なぜHooksが導入されたのか

クラスコンポーネントは難しいから

ベテランエンジニアにとっては慣れているかもしれないが、クラス構文は駆け出しエンジニアには理解しづらい。またJavaScriptのクラスはRubyのようなオブジェクト指向言語のクラスとは違う。JavaScriptのクラスはコンストラクター関数の糖衣構文にすぎないのだ。

コードの記述量を減らすため

クラスコンポーネントには決まり文句のようなコードが多い。そして、その量が無駄に多いのである。しかしHooksを使うとコードの記述量を減らすことができる。

Hooksを使ってみる

import React, { useState, useEffect } from 'react'

function Counter(props) {
  // useStateの引数には初期値を渡す
  // 初期値(state)と値を更新する関数を変数に代入
  const [count, setCount] = useState(0)
  const [name, setName] = useState('')

  // componentDidMount
  // componentDidUpdateと同じ役割
  // つまり、コンポーネントがレンダリングされる度に呼ばれる
  useEffect(() => {
    document.title = `${name}が${count}回クリックしました!`
    // componentWillUnmountはここに書く
    return () => console.log('お掃除しました!')
  })

  return (
    <>
      <input type="text" onChange={e => setName(e.target.value)}/>
      <p>{name}{count}回クリックしました!</p>
      <button onClick={() => setCount(count + 1)}>増やす</button>
    </>
  )
}

export default Counter

useEffectは再利用ができる

useEffect()をラップした新しい関数を作成して別ファイルに切り出す。その関数をimportすれば異なるコンポーネント間での再利用が可能になる。これもHooksを使う大きなメリットになりそう。

【React】高階コンポーネントを使ってみる

f:id:lampler:20210605070153p:plain

高階コンポーネントって何?

高階コンポーネント高階関数と似ている。高階関数とは関数を戻り値とする関数のこと。

高階コンポーネントは引数でコンポーネントを受け取り、戻り値として新しいコンポーネントを返す関数のこと。

どんな時に使うの?

クラスの継承と似ていると思う。親クラスに状態(state)やメソッドを定義して、子クラスで使い回す。似たような処理を機能を持つコンポーネントを複数個作成する時に使える。

サンプルコード

ルートのコンポーネント:App.js

import Movie from './hoc/Movie'

function App() {
  return (
    <Movie id={1}/>
  )
}

export default App

高階コンポーネント:withTooltip.js(withから名前をつけるのが慣例) 引数で受け取るコンポーネントの親になる。

import React from 'react'

function withTooltip(Component) {
  return class withTooltip extends React.Component {
    state = { showTooltip: false}
    mouseOver = () => this.setState({ showTooltip: true })
    mouseOut = () => this.setState({ showTooltip: false })

    render() {
      return (
        <div>
          <p>マウスを乗せると答えが出るよ</p>
          <Component
            onMouseOver={this.mouseOver}
            onMouseOut={this.mouseOut}
            showTooltip={this.state.showTooltip}
            {...this.props} // 親からもらったデータを全て渡す
          />
        </div>
      )
    }
  }
}

export default withTooltip

最下層の子コンポーネント: Movie.js

import React, { Component } from 'react'
import withTooltip from './withTooltip'

class Movie extends Component {
  render() {
    return (
      <div>
        <span>id: {this.props.id}</span>
        <p
          onMouseOver={this.props.onMouseOver}
          onMouseOut={this.props.onMouseOut}
        >
          高級コンポーネントとは?
        </p>
        {this.props.showTooltip && <p>コンポーネントを返す関数だよ</p>}
      </div>
    )
  }
}
export default withTooltip(Movie)
// 戻り値はWithTooltipにラップされたMovieコンポーネント
// つまりMovieコンポーネントを子に持つWithTooltipコンポーネントである

親子関係はこの通り。Appで渡したidもしっかりとMovieへ渡すことができている。

f:id:lampler:20210605065947p:plain

【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には配列ではなくオブジェクトが格納されている。