okinawa

IT勉強メモ

Spring Boot起動時エラー field in required a bean of type that could not be found

同じ状況での解決方法が、ネット上に見つからなかったのでメモ。

現象

Spring Boot起動時に出たエラー。

*************************
APPLICATION FAILED START
*************************
Description:

Field 'MapperInterface名' in 'Serviceクラス名' required a bean of type 'MapperInterface名' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'MapperInterface名' in your configuration.

Serviceクラスの中で使っているMapperInterfaceが見つかりません。

AutowiredでMapperInterfaceを呼び出してますよ。

設定に 'MapperInterface 名' 型の Bean を定義することを検討してください、的なことを言っている。

要するにMapperInterfaceがDIされてないよ、ということ。

状況

SpringBoot2x→3xにバージョンアップ後に起きた。

Mybatisを使っていて、MapperInterfaceに@Mapperが付いていた。

@Mapper
public interface Mapper {  }

@MapperついてたらDIされるのでは???とだいぶ混乱した。

原因

MyBatis Spring Boot Starterのバージョンが古かった

pom.xmlを編集。1.0.2→3.0.3

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

SpringBootの基本的なことがわかってなくて大変だった。

お勉強します。

【React】createAsyncThunk・createSliceメモ

参考

redux.js.org

www.udemy.com

公式のありがたい画像

公式のはGIF画像なので下記画像よりわかりやすい。要参照。
Redux Fundamentals, Part 6: Async Logic and Data Fetching | Redux

画像に出てくる各用語の解説

詳しくは下記に書いた。

dodosu.hatenablog.jp

Store

状態(State)を保管しておく場所。

Reducer

Reducerとはstateをどのように更新するか定義した関数。

State

状態のこと。

Reduxは、このStateを管理するためのライブラリ。

Stateが更新されると画面が再描画される。

UI

関数コンポーネントのこと。

Action

名前はActionだけど何のActionもしない。

Reducerの識別子である「tpye」と「payload(Stateの素)」を持つオブジェクト。

payload

本来のデータ部分のことらしい。

HTTPで言うと、ヘッダデータなどの付加情報をを除いたボディ部分。

APIとの通信の場合、payloadにレスポンスデータが入ってくる。

Dispatch

ReducerにActionを渡して、Reducerを呼び出す関数。ここがスタート。

dispatch(action);でReducer呼び出し。

EventHandler

そのまま。

何らかのEventと紐付ける。ボタンクリックなど。

StoreとEventHandlerの繋ぎ

Dispatchの引数にActionオブジェクトを渡して繋ぐ。

ActionオブジェクトにはReducerの識別子であるtypeプロパティがある。

typeプロパティの値でReducerと紐付ける。

createAsyncThunkとは

Redux Tool kitに含まれる非同期処理を扱うためのツール。

Stateの更新を非同期で行う場合に使う。

APIとの通信によく使われます。

ざっくり言うとこの関数の引数に、Promiseを返す非同期処理を渡すとStateを更新できる。

createSliceとセットで使う。

createAsyncThunkの構文

createAsyncThunk(
  'type',
   非同期関数
)

const addAsyncWithStatus = createAsyncThunk(
  'counter/asyncCount', // 第一引数 = type = 一意な名前なら何でもよい 
  // 第2引数 = 何らかの非同期処理↓
  async (payload) => {
    const response = await asyncCount(payload);
    return response.data;
  }
)

createAsyncThunkの引数

・第一引数:type(文字列)
Reducerを識別するための文字列。一意な名前なら何でもよいらしい。

・第二引数:payloadCreator
Promiseを返す非同期関数のこと。

createAsyncThunkの戻り値

ActionCreatorを返す。

返すActionCreatorは処理状態によって異なります。

  • pendingの時: ‘関数名.pending’
  • fulfilledの時: ‘関数名.fulfilled’
  • rejectedの時: ‘関数名.rejected’

構文のコード例で行くと下記のActioCreatorが返ってくる。

addAsyncWithStatus.pending
addAsyncWithStatus.fullfilled
addAsyncWithStatus.rejected

createSliceとは

createSlice は、Redux Toolkit の一部であり、Reduxの createReducer や createAction を組み合わせたものです。

これにより、簡単にReduxのstoreの状態を更新するためのリデューサーとアクションを作成できます。

簡単に言うと、Reducer・ActioCreator・Stateを持つオブジェクトを返す。

構文

const counter = createSlice({
  name: 'Slice名', //これがtypeの一部になる
  initialState: {state: '初期値'},
  //reducersには同期処理
  reducers: {
   /*Stateを更新する同期処理をここに書く*/ 
  },

  //extraReducersには非同期処理
  extraReducers: (builder) => {
    builder.addCase(addAsyncWithStatus.pending, (state) => {
      //State更新する処理(処理中)
    })
      .addCase(addAsyncWithStatus.fulfilled, (state, action) => {
        //State更新する処理(成功時)
        state.count = state.count + action.payload;
      })
      .addCase(addAsyncWithStatus.rejected, (state) => {
        //State更新する処理(失敗時)
      })
  }
});

引数

  • name=Sliceの名前
  • initilState=stateの初期値
  • reducers=同期処理のReducer
  • extraReducers=非同期処理のReducer

戻り値

公式より引用
createSlice | Redux Toolkit

{
    name: string,
    reducer: ReducerFunction,
    actions: Record<string, ActionCreator>,
    caseReducers: Record<string, CaseReducer>.
    getInitialState: () => State,
    reducerPath: string,
    selectSlice: Selector;
    selectors: Record<string, Selector>,
    getSelectors: (selectState: (rootState: RootState) => State) => Record<string, Selector>
    injectInto: (injectable: Injectable, config?: InjectConfig & { reducerPath?: string }) => InjectedSlice
}

とりあえずactionCreatorとReducerを返すとだけ覚えておけばOK。

actionCreatorとReducerの取得方法↓

const sliceSample = createSlice( /*省略*/);

const [ reducersオブジェクト内の関数名 ] = sliceSample.actions; // reducersのActionCreator取得
const rducerFunc =  sliceSample.reducer; // Reducer取得。これをStoreに渡す

サンプルコード

上がcounter。下がtestSlice。

非同期ボタンを押すと、0~1秒後に計算結果が返ってくるボタン。(extraReducers)

他のボタンは同期処理。(reducers)

・Example.js

import Counter from "./components/Counter";
import { Provider } from "react-redux";
// import store from "./store"
import { createSlice, createAsyncThunk, configureStore } from "@reduxjs/toolkit";

//非同期処理用の関数。0~1秒後に引数の値を返すだけ。
//createAsyncThunkにthunkされる関数
//これの戻り値がextraReducersのaction.payloadに渡される
const asyncCount = (count = 1) => {
  return new Promise((resolve) =>
    setTimeout(() => resolve({ data: count }), Math.random() * 1000)
  );
};

//ActionCreator
//createAsyncThunkはActioCreatorを返す関数
const addAsyncWithStatus = createAsyncThunk(
  'counter/asyncCount', // 第一引数=type
  async (payload) => { // 第2引数=payloadCreator=何らかの値を返す非同期処理の関数
    const response = await asyncCount(payload);
    return response.data;
  }
)

//ActionCreator
//createAsyncThunk2つめ。中身は同じ。
const addAsyncWithStatus2 = createAsyncThunk(
  'test/asyncCount',
  async (payload) => {
    const response = await asyncCount(payload);
    return response.data;
  }
)

/**Reducer&Action&Stateの生成
 * createSliceはReducerとActionCreatorとStateを含むオブジェクトを返す
 */
const counter = createSlice({
  name: 'counter',///Slice名。これがtypeの一部になる
  initialState: {//Stateの初期値
    count: 0,
    status: ''
  },
  //reducersには同期処理
  reducers: {
    add(state, { type, payload }) {
      state.count = state.count + payload;
    },
    minus(state, { type, payload }) {
      state.count = state.count - payload;
    }
  },
  //extraReducersには非同期処理
  extraReducers: (builder) => {
    builder.addCase(addAsyncWithStatus.pending, (state) => {
      state.status = 'Loading...'
    })
      .addCase(addAsyncWithStatus.fulfilled, (state, action) => {
        state.status = '取得済'
        state.count = state.count + action.payload;
      })
      .addCase(addAsyncWithStatus.rejected, (state) => {
        state.status = 'エラー'
      })
  }
});

//2つ目のcreateSlice
//Reducer&Action&Stateの生成
const testSlice = createSlice({
  name: 'test',//Slice名。これがtypeの一部
  initialState: {
    result: 1,
    status2: ''
  },
  reducers: {
    multiplication(state, { type, payload }) {
      state.result = state.result * payload;
    },
    division(state, { type, payload }) {
      state.result = state.result / payload;
    }
  },
  extraReducers: (builder) => {
    builder.addCase(addAsyncWithStatus2.pending, (state) => {
      state.status2 = "処理中";
    })
      .addCase(addAsyncWithStatus2.fulfilled, (state, action) => {
        state.status2 = "成功";
        state.result = state.result * action.payload;
      })
      .addCase(addAsyncWithStatus2.rejected, (state) => {
        state.status2 = "失敗";
      })
  }
});

//Store生成
const store =  configureStore({
  reducer: {
    counter: counter.reducer,//counter.reducerにReducerが入っている
    test: testSlice.reducer//testSlice.reducerにReducerが入っている
  }
});


// actionsにActionCreatorが入っている
// typeが counter/addとcounter/minus
const { add, minus } = counter.actions;

// typeが test/multiplicationとtest/division
const { multiplication, division } = testSlice.actions;


const Example = () => {
  return (
    <Provider store={store}>
// CounterコンポーネントにActioCreatorをすべて渡す
      <Counter actionCreator1={add} actionCreator2={minus} actionCreator3={addAsyncWithStatus}
        actionCreator4={multiplication} actionCreator5={division} actionCreator6={addAsyncWithStatus2} />
    </Provider>
  );
};

export default Example;

・Counter.js

import { useSelector, useDispatch } from "react-redux"

//ExampleコンポーネントからActioCreatorをすべて受け取り
const Counter = ({ actionCreator1, actionCreator2, actionCreator3, actionCreator4, actionCreator5, actionCreator6 }) => {
    const dispatch = useDispatch();

    const clickHandler = (e) => {
        if (e.target.name === "+2") {
            dispatch(actionCreator1(2)); // dispatch(actionCreator)でReducerを呼び出す
        } else if (e.target.name === "-2") {
            dispatch(actionCreator2(2));
        } else {
            dispatch(actionCreator3(2));
        }
    }

        const clickHandler2 = (e) => {
            if (e.target.name === "*2") {
                dispatch(actionCreator4(2));
            } else if (e.target.name === "/2") {
                dispatch(actionCreator5(2));
            } else {
                dispatch(actionCreator6(3));
            }
        }
    
    // useSelector()でStateを取得
    const status = useSelector(state => state.counter.status);
    const count = useSelector(state => state.counter.count);
    const result = useSelector(state => state.test.result);
    const status2 = useSelector(state => state.test.status2);
    return (
        <>
            <p>{count}</p>
            <h3>{status}</h3>
            <button onClick={clickHandler} name="+2">+2</button>
            <button onClick={clickHandler} name="-2">-2</button>
            <button onClick={clickHandler} name="非同期+2">非同期+2</button>
            <p>{result}</p>
            <h3>{status2}</h3>
            <button onClick={clickHandler2} name="*2">×2</button>
            <button onClick={clickHandler2} name="/2">÷2</button>
            <button onClick={clickHandler2} name="非同期*3">×3</button>

        </>
    )
}
export default Counter;

ちょっと分かりづらい所メモ(ActionCreatorとtype)

ActionCreator

Actionとはtypeとpayloadを持つだけの単なるオブジェクト。

このActionオブジェクトを返すのがActionCreatorという関数。

const actionCreator = () => {
  return {
      type: "couter/add", // typeはReducerを特定するための識別子
      paylod: 2  // payloadをReducerに渡して、これを元にStateを更新する
    }
}

■ActionCreatorの取得

同期処理のActionCreatorはcreateSliceの戻り値から取得。

非同期処理のActionCreatorはcreateAsyncThunkの戻り値から取得。

// createSliceからActionCreator関数を取得
const counter = createSlice(/*省略*/);
const { add, minus } = counter.actions;

// createAsyncThunkからActionCreator関数を取得
// 非同期処理関数の戻り値(pending/fullfilled/rejected)に応じた下記のActionCreatorを自動生成
// addAsyncWithStatus.pending
// addAsyncWithStatus.fullfilled
// addAsyncWithStatus.rejected
const addAsyncWithStatus = createAsyncThunk('type', 非同期処理関数);

reducersとextraReducersのtypeの違い

■reducersのtype(識別子)は「Slice名/Reducer名」

  • counter/add
  • counter/minus
  • test/multiplication
  • test/division

createSliceの戻り値であるActionCreatorにtypeが含まれている。

const { add, minus } = counter.actions; // typeはcounter/addとcounter/minus

■extraReducerのtype(識別子)は「createAsyncThunkの第一引数(type)/状態」

  • counter/addAsyncWithStatus/pending
  • counter/addAsyncWithStatus/fulfilled
  • counter/addAsyncWithStatus/rejected
  • test/addAsyncWithStatus2/pending
  • test/addAsyncWithStatus2/fulfilled
  • test/addAsyncWithStatus2/rejected

createAsycThunk関数の戻り値がActionCreator。

状態によってpending/fullfilled/rejectedが自動生成される。

const addAsyncWithStatus = createAsyncThunk('type', 非同期処理関数);

React hook基礎

参考

qiita.com

qiita.com

qiita.com

React hookとは

状態管理や副作用などのReactの機能を、クラスを書かずに使えるようにする機能。

かつては状態管理や副作用を使うにはクラスコンポーネントとというものを利用していたらしい。

関数コンポーネントの純粋性を保つために、状態管理や副作用を、関数コンポーネント内ではなく、React hookに任せる。

React hookのイメージ

・参考
初心者向け React フックの基礎

Reach hookを理解するにはコンポーネントが関数であることを思い出すと理解しやすい。

React hookは基本的に関数の外にあるものを管理する。

例えば、Stateは関数外の変数。

関数コンポーネントはステートレスな純粋関数だから、Stateは関数外部で管理するということらしい。

useState()は関数外部のgetter/setterを呼び出しているイメージ。

でも、純粋関数って関数外部の変数を参照も変更もしないはず。。。よくわからず。

前提として知っておきたいこと

1,関数コンポーネントはStateが更新される度に再実行(再レンダリング)される。
→useMemoで必要な知識

2.親コンポーネントが再実行(再レンダリング)されると、子コンポーネントも再実行される。

3.コールバック関数を Props として受け取った関数コンポーネントは必ず再レンダリングされる。
→useCallbackで必要な知識

→useMemoを使えば再レンダリングされなかった。React.memoだとされる。

4.JSXの <Child /> はコンポーネント呼び出し
関数呼び出しと同じ意味。

5.再レンダリング=関数コンポーネント再実行

6.関数コンポーネント再実行されると、関数内のオブジェクト(コールバック関数含む)は再生成され、参照値が変わる。→React.memoで必要な知識

//Example関数実行の度に、中のオブジェクトは再生成され、参照値が変わる
const Example = () => {
  const obj = {name: "takeshi", age:20};// オブジェクト
  const callbackFunc = () => {
    console.log("関数オブジェクト");
  }
  return (
    <h3>abc</h3>
    );
};

useState

useStateは、関数コンポーネント内で状態(State)管理を行うためのフックです。具体的には、変数を扱うことができます。

状態(State)を更新すると、関数コンポーネントが再実行(再レンダリング)され、即座にブラウザ上にも反映されます。

・構文

const [状態変数, 状態更新用関数] = useState(状態の初期値);

const [state, setState] = useState(0);//初期値0で初期化
seState(1); // stateを1に更新

コード例

setCountメソッドを使ってcountの値を更新できます。例えば、ボタンをクリックしたらcountを増減させることができます。

    import React, { useState } from 'react';
    function Counter() {
      const [count, setCount] = useState(0);//初期化
      const [str, setStr] = useState("Hello!");//文字列も可

      const increment = () => setCount(count + 1);//状態更新
      const decrement = () => setCount(count - 1);//状態更新

      return (
        <div>
          <h1>Counter</h1>
          <h2>カウント: {count}</h2>
          <button onClick={increment}>+</button>
          <button onClick={decrement}>-</button>
        </div>
      );
    }

Stateの特徴と注意点

■特徴

  • ステートが変わると再レンダリングされる(重要!!!)
  • ステートは再レンダリング後も保持。F5で画面全体再更新すると消える。
  • ステートはコンポーネント毎に保持
  • ステートはpropsで渡せる
  • ステートを更新する時は更新用関数を使う

■注意点

  • コンポーネントの中で呼び出す
  • if/for文の中で呼び出さない
  • 状態更新は即時ではない(予約)
  • オブジェクト型のステート変更する時は全プロパティを変更する
  • オブジェクト型と配列のステート変更時は、新しいオブジェクトを渡す
  • コンポーネントが消えるとステートも初期化される

Stateについて詳しくはこちら↓
React基礎 - okinawa

useEffect

useEffectは、関数コンポーネント内で副作用を実行するためのフックです。

副作用とは、fetch関数を使って外部のリソースからデータを取得したり、DOMの更新、ロギング(console.logも含む)などの処理を指します。

・構文

useEffect(() => {
  // 依存配列変更後に実行したい処理
  console.log("副作用処理")
},[依存配列]);

依存配列に変更があった時のみ、useEffect内の処理が実行されます。

依存配列未指定の場合は、初レンダリング時のみ実行されます。

useEffectの使い所

State変更後に何らかの処理を実行したい時。

コード例

ボタン押す度にcountが変更し、useEffect内の処理が実行される。

const Example = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("useEffect内実行");
  }, [count]);//Stateであるcountが依存配列

  return (
    <>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>ボタン</button>
    </>
  )

}

export default Example;

useMemo

useMemoは関数の計算結果を保存する。

関数自体を保存するわけではなく、値だけを保存する。(useCallbackは関数自体を保存する)

数値でも文字列でもJSXでもなんでも保存できますよ。

・構文

  useMemo(() => {
    return 重い処理
  },[依存配列])

依存配列に変更があったときのみ、useMemo内の処理が実行される。
そして、return結果がMemoに保存される。
依存配列未指定の場合は、初回レンダリング時のみ実行される。

useMemoの使い所

重い処理の結果を保存しておき、再利用する。

例えば、関数Aの中で2つの関数を呼び出していたとして

  • 処理の重い関数B
  • 処理の軽い関数C

関数Aを呼び出すと、BもCもどちらも実行される。

でも、重いBは必要なときだけ呼び出したいんだよなあ、という時に使う。

function 関数コンポーネントA() {
  // 関数Bの処理は非常に重いので、必要なときだけ呼び出したい
  関数B();

  // 関数Cの処理は軽いので、毎回呼び出してOK
  関数C();
}

例:5億回のループは必要なときだけにしたい

・useMemoを使わない場合

軽い処理ボタンを押すと、lightStateが更新される。

Stateが変わると再レンダリング、つまり関数Exampleが再実行されるので、heavyFunc(重い処理)も呼び出されてしまい、レンダリングに時間がかかる。

import { useEffect, useCallback, useMemo, useState } from 'react';

//Stateが更新されるとExample関数が再実行される
const Example = () => {
  const [heavyState, setHeavyState] = useState(1);
  const [lightState, setLightState] = useState(0);

  const lightFunc = () => {
    setLightState(prev => prev + 1);
    return lightState;
  }

  //heavyState * 5億回のループ
  const heavyFunc = (heavyState) => {
    let result = 0;
    for (let i = 0; i <= heavyState * 500000000; i++) {
      result = i;
    }
    return result;
  }

  // レンダリングの度に重い処理を呼び出す
  const result2 = heavyFunc(heavyState);

  return (
    <>
      <p>{lightState}</p>
      <button onClick={lightFunc}>軽い処理</button>
      <p>{heavyState} * 500000000 = {result2}回のループ</p>
      <button onClick={() => setHeavyState(prev => prev + 1)}>重い処理</button>
    </>

  );
};

export default Example;

↓ useMemo()を使うバージョン

useMemoでheavyFuncの結果を保存して再利用可能にしておく。

heavyFuncは、heavyStateの値が変わった時、つまり重い処理ボタン押したときのみ実行される。

import { useEffect, useCallback, useMemo, useState } from 'react';

//Stateが更新されるとExample関数が再実行される
const Example = () => {
  const [heavyState, setHeavyState] = useState(1);
  const [lightState, setLightState] = useState(0);

  const lightFunc = () => {
    setLightState(prev => prev + 1);
    return lightState;
  }

  //heavyState * 5億回のループ
  const heavyFunc = (heavyState) => {
    let result = 0;
    for (let i = 0; i <= heavyState * 500000000; i++) {
      result = i;
    }
    return result;
  }

  // 変更箇所はここのみ
  // useMemoで結果をキャッシュする
  // Example関数が再実行されても、heavyState変わってなければ、再実行されない
  // 再実行しない場合はキャッシュした結果を使う
  const result2 = useMemo(() => {
    return heavyFunc(heavyState);
  },[heavyState])


  return (
    <>
      <p>{lightState}</p>
      <button onClick={lightFunc}>軽い処理</button>
      <p>{heavyState} * 500000000 = {result2}回のループ</p>
      <button onClick={() => setHeavyState(prev => prev + 1)}>重い処理</button>
    </>

  );
};

export default Example;

useMemoの注意点

コンポーネントをメモ化する場合は注意。

特にコンポーネント丸ごとuseMemoに入れちゃえばええやん!とやるとハマりがち。

以下のコードは、ボタンクリックでコンソールに「click」と表示するものだが、機能しない。

なぜなら、ボタンが丸ごとメモ化されているので、依存配列が変わらない限り、useMemo内の処理は実行されない。

こういう時はReact.memoを使う。

import React, { useEffect, useCallback, useMemo, useState } from 'react';

function Child({handleClick}) {
  const memo = useMemo((handleClick) => {
    return <button onClick={handleClick}>Child</button>; 
  },[handleClick]);//依存配列に関数を入れれば行けそうな気がするがダメ。クリックしても関数自体が変化するわけではないので
  return memo;
}


export default function App() {

  const handleClick = () => {
    console.log("click");
  };

  return (
    <>
      <Child handleClick={handleClick} />
    </>
  );
}

React.memoとは

※React.memoはhookではないが、useMemoと似ていてごっちゃになるので記載する。

React.memoは不要な再レンダリングを防ぐためのもの。

コンポーネントのpropsが変更された場合のみ、再レンダリングを行う。

propsが変更されない限り、前回のメモを再利用して再レンダリングを回避する。

React.memoとuseMemoの使い所

・使い所イメージ(React.memo)

親コンポーネントA {
  // 重いレンダリング処理B
  子コンポーネントB()

  // 軽い処理
  関数C()
}

親実行時に子Bが再レンダリングされるのを回避したいとき使う。-

・React.memoとuseMemoの再実行条件
useMemoは依存配列が変わった時のみ。

React.memoはpropsが変わった時。ちと複雑。

・propsの変更判定について
propsの値が変わってなくても、参照値が変わっていれば変更したと判定される。

例えば、親コンポーネント関数が再実行されると、親から渡されるpropsも再生成されるので、参照値が変わる。

参考
qiita.com

コード例

・React.memo未使用↓
parentボタンを押すとChildコンポーネントが再レンダリングされてしまう。

function Child() {
  console.log("Child再実行");
  return <p>Childコンポーネント</p>
}

export default function App() {

  const [count, setCount] = useState(0);

  const countUp = () => {
    setCount(prev => prev + 1);
  }

  return (
    <>
      <p>{count}</p>
      <button onClick={countUp}>parent</button>
      <Child />
    </>
  );
}

・React.memo使用↓
parentボタン押しても、Chilidが再レンダリングされない。

const Child = React.memo((props) => {
  console.log("Child再実行")
  // コンポーネントをメモ化
  return (
    <>
      <p>Childコンポーネント</p>
    </>
  );
});

export default function App() {

  const [count, setCount] = useState(0);

  const countUp = () => {
    setCount(prev => prev + 1);
  }

  return (
    <>

      <p>{count}</p>
      <button onClick={countUp}>parent</button>
      <Child />
    </>
  );
}

React.memoとuseMemoの違いまとめ

どっちも結果をメモする機能。

  1. useMemo:

    • useMemoは、値をメモ化するためのReactフックです。
    • 計算結果の値をキャッシュし、同じ計算を繰り返さないようにします。
    • 例えば、重い計算やデータの加工が必要な場合、その結果を保持して再利用できます。
    • 依存配列の変更に応じて再計算を行います。
  2. React.memo:

  3. useMemoの例:

    const expensiveValue = useMemo(() => {
      // 重い計算やデータの加工
      return computeExpensiveValue();
    }, [dependency1, dependency2]);
  • React.memoの例:
    const MyComponent = React.memo(props => {
      // レンダリングコストが高いコンポーネント
      return <div>{props.data}</div>;
    });

React.memoの詳細についてはこちら↓
React.memo / useCallback / useMemo の使い方、使い所を理解してパフォーマンス最適化をする #JavaScript - Qiita

両者の違いはこちらがわかりやすい↓
yukimasablog.com

React.memoの注意点(意図しない再レンダリング

propsの値が変わるとメモを使用せず、再実行してしまう。

中身の値が変わらず、参照値が変わるだけでも再実行。こういう時はuseCallbackを使うと再実行を防げる

import React, { useEffect, useCallback, useMemo, useState } from 'react';

const Child = React.memo(({func}) => {
  //メモ内に親から渡されたオブジェクトがあるので、親再実行すると、memo内処理も実行される。
  console.log("メモ内実行");
  return <button onClick={func}>Child</button>;
});

export default function App() {
//再レンダリング時に関数オブジェクトが再生成され、参照値が変わる
  const callbackFunc = () => {
    console.log("関数オブジェクト");
  }
  return (
    <Child object={func={callbackFunc} />
  )

面倒だから全部useMemoに統一したいけどダメ?

残念ながらダメ。

useMemoの注意点で書いたケースはReact.memoを使う。

useCallback

※React.memoとの併用で使う。useMemoしか使っていないならuseCallbackの使い所はほぼない。

React.memoの特徴としてメモ内のpropsに変更があると、再レンダリングされる。

例えばコールバック関数がpropsとして渡されていると、親コンポーネントレンダリング→propsも再生成で参照値変わる→React.memoしてても子コンポーネントレンダリング

これを防ぐためにuseCallbackを使う。

詳しくは、リンクの「Reactコンポーネントの再レンダリング条件検証」を見るとわかる。

useCallbackの使い所

React.memoしているのに、子コンポーネントが再レンダリングしてしまうのを防ぎたい時。

関数をメモ化(保存)することで、参照値が変わらないようにする。

・使用方法
子にpropsとして渡すコールバック関数を親側でメモ化しておき、親再レンダリング時にコールバック関数を再生成しない。

そうすると、メモされたコールバック関数は、毎回同じオブジェクト判定なので、子のReact.memoに渡しても、React.memo内が再実行されない。(子が再レンダリングされない)

・構文

const memoCallbackFunc = useCallback(() => {
    // 第一引数は何らかの処理
    console.log("あいうえお");
  },[依存配列])

依存配列に変更があったときのみ、useCallback内の処理を保存し直す。(処理を実行はしない)
処理自体が保存されるので、returnはなくてもよい。
依存配列未指定の場合は、初回レンダリング時のみ保存される。

・useCallback未使用の例

参考
React.memo / useCallback / useMemo の使い方、使い所を理解してパフォーマンス最適化をする #JavaScript - Qiita

import React, { useState } from "react";

const Child = React.memo(props => {
  console.log("render Child");
  return <button onClick={props.handleClick}>Child</button>;
});

export default function App() {
  console.log("render App");

  const [count, setCount] = useState(0);
  // 関数はコンポーネントが再レンダリングされる度に再生成されるため、
  // 関数の内容が同じでも、新しい handleClick と前回の handleClick は
  // 異なるオブジェクトなので、等価ではない。
  // そのため、コンポーネントが再レンダリングされる。
  const handleClick = () => {
    console.log("click");
  };

  return (
    <>
      <p>Counter: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment count</button>
      <Child handleClick={handleClick} />
    </>
  );
}

・useCallback使用例

参考

React.memo / useCallback / useMemo の使い方、使い所を理解してパフォーマンス最適化をする #JavaScript - Qiita

import React, { useState, useCallback } from "react";

const Child = React.memo(props => {
  console.log("render Child");
  return <button onClick={props.handleClick}>Child</button>;
});

export default function App() {
  console.log("render App");

  const [count, setCount] = useState(0);
  // 関数をメモ化すれば、新しい handleClick と前回の handleClick は
  // 等価になる。そのため、Child コンポーネントは再レンダリングされない。
  const handleClick = useCallback(() => {
    console.log("click");
  }, []);

  return (
    <>
      <p>Counter: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment count</button>
      <Child handleClick={handleClick} />
    </>
  );
}

まとめ

  1. useState:状態管理
  2. useEffect:状態変更後に実行したい処理
  3. useMemo:重い処理の結果保存
  4. React.memo:重いコンポーネント保存
  5. useCallback:コールバック関数保存。React.memoと併用

React.memoとuseCallbackの関係だけややこしい。

  • React.memoとuseCallbackはセットで使うよ
  • React.memoの再実行条件はpropsオブジェクトに変更があった時
  • propsオブジェクトは、参照値が変わっただけで変更したと判定されるよ
  • だからuseCallback使って参照値も変わらないようにするよ

Reduxの概要

Reduxとは

状態を管理するライブラリ。

アプリケーション全体で状態を管理できるため、propsで渡す手間が省ける。

propsのバケツリレーを回避できる。

React以外でも使える。

参考サイト

www.udemy.com

redux.js.org

qiita.com

素のReduxコードと公式画像を照らし合わせて理解する

素のReduxは非推奨。

実運用では、Redux tool kitを使うこと。

公式画像と照らし合わせながら見て理解を深めたい。

ファイルサイズ大きくて公式GIFを貼れなかったので下記を参照↓
Redux Fundamentals, Part 2: Concepts and Data Flow | Redux

Reduxデータフロー図

最初にまとめ

  1. ボタンクリック(EventHandler起動)
  2. dispatch(action{type:~, payload:~})でActionをReducerに送信
  3. typeを元にどのReducerを起動するか識別
  4. Reducerがpayloadを使ってStateを更新
  5. StateがUIに反映される

Store

状態を保管しておく場所。

StoreはStateとReducerを持つ。

createStore(Reducer);でStore全体を生成。

import { createStore } from "redux";

const initialState = 0;
const reducer; //省略

export default createStore( // ここでStore生成
    reducer
);

Reducer

Reducerとはstateをどのように更新するか定義した関数。

createStore(Reducer);で引数にReducerを渡す。

そうするとStoreの中にReducerが作られる。Reducerは複数生成可能。

import { createStore } from "redux";

const initialState = 0;
const reducer = (state = initialState, { type, payload }) => { //Reducerを定義
    switch (type) {
      case "+":
        return state + payload ;
      case "-":
        return state - payload ;
      default:
        return state;
    }
  };

export default createStore( // ここでStore生成
    reducer
);

State

状態のこと。

状態とはWebアプリだと、ログイン状態やカート内商品の個数など。

StateはReducerと同じ数作られる。

Reduxは、このStateを管理するためのライブラリ。

useSelector()でStateを取り出す。

Stateが更新されると画面が再描画される。

import { useSelector } from "react-redux"

const state = useSelector(state => state);//Stateの取得

UI

関数コンポーネントのこと。

useSlector();で更新後のStateを取得する。

Stateが更新されると、画面が再レンダリングされる。

Action

名前はActionだけど何のActionもしない。

Reducerの識別子である「tpye」と「payload(Stateの素)」を持つオブジェクト。

type識別子でReducer関数を特定 → payloadの値を使ってReducerがStateを更新する、という流れ。

ActionはDisptchの引数として、Reducerに渡される、Actionを元に実際にアクションするのはReducer。

import { useDispatch } from "react-redux";

    const dispatch = useDispatch();

    const eventHandler = () => {
        dispatch({ type: "+", payload: 1 });
// type(+ or -)でReducerのswitch文の分岐を判定。
// paylod(数値)を元にRducerがStateを更新。足し算引き算。
// typeとpayloadが含まれたオブジェクトをActionオブジェクトと言う
    }

type

Reducerを識別するための名前みたいなもの。

Reducerには1個1個この識別子が付いてる。

payload

本来の意味は

  • 有料荷重(貨物のうちの有料ブ部分)
  • 乗客と貨物の最大積載量

IT用語では、本来のデータ部分のこと。

HTTPで言うと、ヘッダデータなどの付加情報をを除いたボディ部分。

APIとの通信の場合、payloadにレスポンスデータが入ってくる。

太字部分↓
http://www.google.com?name="yamada"

ReduxではActionにpaylodが含まれていて、dispatch(action);でReducerを呼び出し、Reducerにpayloadが渡される。

Dispatch

ReducerにActionを渡して、Reducerを呼び出す関数。ここがスタート。

const dispatch = useDispatch();で生成。

dispatch(action);でdispatchの引数にAcitonオブジェクトを渡して実行する。

import { useDispatch } from "react-redux";

const CounterButton = ({type, payload}) => {

    const dispatch = useDispatch();

    const eventHandler = () => {
        dispatch({ type, payload });
    }

EventHandler

そのまま。

何らかのEventと紐付ける。ボタンクリックなど。

import { useDispatch } from "react-redux";

    const eventHandler = () => {
        dispatch({ type, payload });
    }

StoreとEventHandlerの繋ぎ

Dispatchの引数にActionオブジェクトを渡して繋ぐ。

ActionオブジェクトにはReducerの識別子であるtypeプロパティがある。

typeプロパティの値でReducerと紐付ける。

import { useDispatch } from "react-redux";

    const eventHandler = () => {
        dispatch({ type, payload });//これ
    }

Findyの紹介プログラム

Findy紹介プログラム

Findyには紹介プログラムがあって、紹介した人と紹介された人の両方にAmazonギフト2000円分が入ります。(月に3人まで)

ただし、Findyに登録後、経歴書をしっかり目に書かないと適用されないようです。

・紹介コード

ym8GoqNg0sdc7

私が登録したときは、担当者との面談をするとAmazonギフト1万円分というキャンペーンもやっていて、けっこう美味しかったです。

転職するときにはお世話になろうと思っているサービスの1つ。

他には転職ドラフトも良さげで、こちらの経歴を見た上で、任せたい仕事や年収の提示があり、ミスマッチが減らせそうなサービス。

そういえばWebアプリってどうやって起動しているんだろうか?

Javaの場合はmainメソッドが最初に動くんだよなあ、くらいの認識しかなかったので少し調べてみた。

サーブレットJava Servlet)の場合です。

起動の流れ

サーブレットコンテナとは?

まずは必要な前提知識を。

サーブレットコンテナとは、サーブレットを動かすのに必要なソフトウェア。代表的なものはTomcat

サーブレットコンテナもJavaで作られている。

mainメソッドはいつ呼び出しているのか?

Javaプログラムのエントリポイントは、public static void main(String[] args)。

どんなプログラムにも必ずあると思っていたが、他のJavaプログラムから呼び出されるなら必要ない。

Tomcatがmainメソッドを持っていて、Tomcatからサーブレットが呼び出されるのでサーブレットはmainメソッドを持っていない。

起動からの流れ(合っているか怪しい)

  1. Tomcat起動(Javaコマンド打ってmainメソッド呼び出し)
  2. Tomcatのmainメソッドが実行される
  3. サーブレットインスタンス化され、サーブレットのinitメソッド実行
  4. HTTPリクエスト待機状態に入る

この状態が、Eclipsetomcat起動した時と同じ状態だと思う。

リクエストが来たらどうなる?

  1. HTTPリクエストが来る
  2. サーブレットがGetかPOSTか判断する
  3. doGet/doPostメソッドが呼び出される

どのクラスのdoGet/doPostを呼び出すかは、web.xmlに「このURLにリクエスト来たらこのクラスを呼び出してね」と書いてある。

こういう流れで起動するから、開発者からするとdoGet/doPostがエントリーポイントのように見える。

インスタンス化とは

newして、メモリ領域に入れること。

Tomcatのmainメソッド

ここから始まる。BootStrapクラス。

public static void main(String args[]) {

        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    log.error("Init exception", t);
                    return;
                }
                daemon = bootstrap;
            } else {

//以下省略

github.com

参考

6.1 サーブレットの制御

いまさら聞けないサーバーサイドJava 第2回 サーブレットとJSPを使いこなすコツ | ウルシステムズ株式会社

Java Solution FAQ:サーブレットの動作原理を教えてください

Servletコンテナとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

Git基礎

参考サイト

Udemy教材。わかりやすくて良かった。
Gitの仕組みも理解できた。
www.udemy.com

Gitコマンド一覧

Gitコマンド虎の巻

用語

ワークツリー

ソースが置いてある、ローカルの作業ディレクトリ。ワークスペース。 git initした場所。

ステージングエリア

スナップショットの保存場所。

ステージングエリアはなぜ必要?
コミットの内容を選択するため。

基本的には1つのコミットに1つの目的。
例:バグ修正と機能追加の2つの修正を分けてステージングしてからコミットする

ローカルリポジトリ

スナップショットの保存場所。

リモートリポジトリ

複数人で共有するリポジトリ

スナップショットの保存場所。

ワークツリーからリモートリポジトリまでの流れ

ワークツリー → add →ステージングエリア → commit → ローカルリポジトリ → push → リモートリポジトリ

.gitフォルダの中にステージングエリアとローカルリポジトリが入っている。

.gitignoreとは

バージョン管理対象外のファイルやディレクトリを管理。

.gitignoreファイルに、ファイル名やディレクトリ名を記述するだけ。

ブランチ関連

trunk

trunk(木の幹)はプロジェクトのメインとなるブランチ。

メインブランチとも言う。

基本的には常にリリース可能な状態にしておく。

トピックブランチ

機能追加やバグ修正などの特定のトピックに基づいたブランチ。

マージ

マージとは2つのブランチを1つに統合すること。

リモート追跡ブランチとは?

リモートリポジトリのブランチの位置を記憶する、ローカルリポジトリのブランチのこと。

ローカルからリモートのブランチを確認するためにある。

$ git branch -a  //ローカルとリモートのブランチ参照
  add-feature
* main
  remotes/origin/HEAD -> origin/main //remotesから始まるのが追跡ブランチ
  remotes/origin/add-feature
  remotes/origin/main

・リモート追跡ブランチの実体

.git\refs\remotes\origin\HEAD ←ここにある

HEADファイルの中身を見るとmainブランチを指している↓

ref: refs/remotes/origin/main

refs/remotes/origin/mainの中身はハッシュ値

74c32143cae0adf61217ed4ba9d351f70e74ff28

リモートリポジトリ名:origin
ブランチ名:main
コミットのハッシュ値:74c32143cae0adf61217ed4ba9d351f70e74ff28

上記がリモート追跡ブランチのHEAD(ポインタ)が指し示すコミット。

上流ブランチ

ローカルブランチに対応するリモートブランチのこと。

通常はローカルとリモートのブランチ名は同じ。

git branch -vvで対応関係が見れる。

$ git branch -vv
* bug-fix 027cf1f [origin/bug-fix] add file.txt //ローカルbug-fixはorigin/bug-fix(上流ブランチ)と対応
  main    22c4913 [origin/main] add 333 conflict solve//ローカルmainはorigin/main(上流ブランチ)と対応

$ git branch -a //全ブランチ参照
* bug-fix
  main
  remotes/origin/HEAD -> origin/main
  remotes/origin/bug-fix
  remotes/origin/main

commitとbranchの仕組み(重要)

現在どのブランチのどのコミットを参照しているかは、HEADで管理している。

HEADとは?
現在のコミット or ブランチを指し示すポインタのこと。

branchとは?
ブランチ内で現在のコミットを指し示すポインタのこと。

.gitディレクトリのHEADファイルを見てポインタを理解する

ポインタはHEADファイルで管理されている。

例1.bug-fixブランチにいるときのHEADファイル

ref: refs/heads/bug-fix

HEADが示すポインタはbug-fixブランチであるとわかる↑

次にrefs/heads/bug-fixファイルを見てみよう。

e07aee2b0bdf1992ca2c94f52a759f9be734a620

↑bug-fixブランチのポインタが示す、コミットのハッシュ値が書かれている

例2.下記のコマンドを実行後にHEADファイルを見てみる

git checkout a547125 //過去のコミットに移動
a547125b295e4cb785c7e4aca08778157ac97dd9

HEADファイルにコミットのハッシュ値が直接書かれている↑

以上のことから下記がわかる。

HEADには現在のコミット値 or 現在のブランチのパスが書かれている。

パスが書かれている場合は、そのパス(refs/heads/bug-fix)に書かれているコミット値が現在のポインタ。

detached HEADとは

HEADがどのブランチも指し示していない状態。

つまり、HEADファイルにコミットのハッシュ値が書かれている状態のこと。

ブランチを指しているときは、HEADファイルにブランチのパスが書かれている。

下記コマンドでできる。

git checkout ハッシュ値 //過去のコミットに移動

$ git checkout c82d
Note: switching to 'c82d'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at c82dc14 333を追加

commitの親子関係

コミットの親子関係

親:1つ前のコミット
子:1つ後のコミット

子は親がどれかわかるが、親は子がどれかわからない。

なぜなら、子から親への参照値はあるが、親から子への参照値はない。子から親への一方通行。

過去の変更履歴を調べたい時

git log --stat //変更履歴を表示。変更したファイル名も表示
git log --online --graph --all //視覚的に見やすいログ表示
git checkout ハッシュ値 //過去の変更に移動
git diff ハッシュ値1(古) ハッシュ値2(新) //差分表示

$ git diff 73df c82dc
diff --git a/README.md b/README.md
index 5386dfe..fb5bed9 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,5 @@

 新機能追加

-Githubから編集111
\ No newline at end of file
+Githubから編集111
+333
\ No newline at end of file

過去の変更に移動するとワークツリーのファイルも過去のものに置き換わるので、過去時点のファイルを参照できる。

コミットの取消方法は2つ

コミットを打ち消す git revert

最新のコミットの後に、過去のコミットを追加して打ち消す。

こっちの方がよく使われる。取消しが履歴として残るのが良い。

・例
コミットA コミットB コミットC(最新)があったとする。

コミットCの後にコミットB'を追加してコミットCをなかったコトにする。

git revert HEAD //最新コミットを1つ前に戻す

コミットA コミットB コミットC コミットB'(最新)となる。

コミットを削除 git reset

//基本はhardオプションで良さそう

git reset --hard ハッシュ値 //hardはワークツリーとステージングエリアにHEADの内容をコピー

git reset --soft ハッシュ値 //softはワークツリーとステージングエリアにHEADの内容をコピーしない

内部的にはコミットを削除しているわけではなく、ポインタを移動しているだけ。

例:bug-fixブランチでgit resetした場合

refs/heads/bug-fixに書かれているハッシュ値が変更される。

通常はbranch内の最新のコミットのハッシュ値が書かれているが、それが古いコミットのハッシュ値に変わる。

移動しているだけなので、元のコミットに戻ることも可能

git reset --hard 最新コミットのハッシュ値 //元に戻す 

共同開発

集中型と分散型

SVNは集中型。
集中型はリモートリポジトリのみ。
単純だがコミットの影響が強い。

Gitは分散型。
分散型はローカルリポジトリとリモートリポジトリがある。
複雑だがコミットはローカルリポジトリにされるので、影響小さい。

共同開発の流れ

上から順に実施する。

git clone リモートリポジトリURL //リモートリポジトリコピー

token作成(必要な場合)  githubのsettings→developer settings

git checkout -b ブランチ名 //トピックブランチ作成。現在のブランチから枝分かれするブランチを作る。ブランチの作成基準は現場によりけり。

何らかの修正作業をする。

git commit -a -m "修正完了" //add commit同時コマンド

git remote //リモートリポジトリ名を確認

---ここから。push前にリモートの変更をローカルに取り込む

git fetch リモートリポジトリ名 リモートのブランチ名 //更新。リモートリポジトリの変更をローカルリポジトリに取り込む。ただし、ローカルのHEADは変わらないので、fetchだけだと変更してないように見える。ワークツリーには影響なし。

git branch -vv //リモートとローカルの対応ブランチ確認。通常はリモートとローカル同じブランチ名。上流ブランチと言う。

git merge origin(リモートリポジトリ名)/main(リモートのブランチ名) // fetchした変更を取り込む。ローカルのブランチを移動してから行うこと。取り込むブランチがmainならmainに移動後。

git pull origin(リモートリポジトリ名) main(リモートのブランチ名)//fetch + merge。普段はこっちを使う。毎日orプルリク前に実行。コンフリクト発生時は修正。

---ここまで

git push リモートリポジトリ名 ローカルのブランチ名:(省略可)リモートのブランチ名 //リモートリポジトリに反映。

コンフリクト発生したら
 コンフリクト修正
 再度 add commit pushする

git branch -a //push成功確認。リモートリポジトリのブランチの位置確認。リモート追跡ブランチ。

git log --graph --online //push成功確認2。ローカルリポジトリとリモートリポジトリのHEADの位置が同じならOK。

$ git log --graph --oneline
* 74c3214 (HEAD -> main, origin/main, origin/HEAD) edit on github //ローカルとリモートのHEAD位置が同じ
*   a884db8 Merge pull request #1 from parapore/add-feature
|\
| * 20ce4de (origin/add-feature, add-feature) fix bug add-feature
| * c2e2d51 add feature README
|/
* c2ab11f Initial commit

プルリクエスト送る
 github画面からプルリクエスト選択→ブランチ選択→create pull request→コメント書く→create pull request

プルリクエストに対して修正が入ったら?
 再度修正、add,commit&PUSH。プルリクエストは送らなくてよい。

git push --delete origin(リモートリポジトリ名) add-feature(削除するブランチ名)//不要になったトピックブランチを削除する。やらなくてよいかも?

コンフリクトが発生した場合は?(merge)

コンフリクトを解消してファイル保存。

git merge ブランチ名  //←ここでコンフリクト発生
コンフリクトを手動修正
git add ファイル名 //mergeではなくadd
git commit -m ”コンフリクトを修正”

マージというのは、別のブランチの変更をHEADブランチにadd commitしている。
そのため、再度mergeは必要なく、add commitのみでOK。

コンフリクトが発生した場合は?(pull)

$ git pull origin main //コンフリクト発生
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 688 bytes | 172.00 KiB/s, done.
From https://github.com/parapore/git-remote-dev
 * branch            main       -> FETCH_HEAD
   73dfa15..c657740  main       -> origin/main
error: Your local changes to the following files would be overwritten by merge: 
        README.md
Please commit your changes or stash them before you merge. //コミットしろと言っている
Aborting
Updating 73dfa15..c657740

$ git commit -a -m "333を追加" //コミット
[main c82dc14] 333を追加
 1 file changed, 2 insertions(+), 1 deletion(-)

$ git pull origin main //再度pullでコンフリクト内容がエディタに表示される
From https://github.com/parapore/git-remote-dev
 * branch            main       -> FETCH_HEAD
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

↓ コンフリクト内容がエディタに表示される

コンフリクト発生時のエディタ

vscode

コンフリクト解消

vscode2

コミットとプルリクエストのタイミング

コミットは切りの良いところで。
1ファイルの変更が終了したらとか、1日の終わりとか。

プルリクエストのタイミングは現場次第。
完了してからの場合もあれば、途中でプルリク送ってチェックしてもらいながらのケースも有り。

コマンド

git init

カレントディレクトリがワークツリーとして設定される。

.gitというディレクトリが作成される。

.gitディレクトリがステージングエリアやローカルリポジトリの実体。

git log

all:全てのコミット履歴を表示。本来見れない子コミット(未来分)も。

oneline:コミットメッセージとHASH値のみ

graph:コミットの枝を視覚的に表示

onelineとgraph療法指定すると見やすいログになる。

$ git log --oneline
74c3214 (HEAD -> main, origin/main, origin/HEAD) edit on github
a884db8 Merge pull request #1 from parapore/add-feature
20ce4de (origin/add-feature, add-feature) fix bug add-feature
c2e2d51 add feature README
c2ab11f Initial commit


$ git log --graph
* commit 74c32143cae0adf61217ed4ba9d351f70e74ff28 (HEAD -> main, origin/main, origin/HEAD)
| Author: parapore <53287775+parapore@users.noreply.github.com>
| Date:   Sun Jan 7 11:35:14 2024 +0900
|
|     edit on github
|
*   commit a884db8ba5078dc0134307e96cd12f450f1857ea
|\  Merge: c2ab11f 20ce4de
| | Author: parapore <53287775+parapore@users.noreply.github.com>
| | Date:   Sun Jan 7 10:54:02 2024 +0900
| |
| |     Merge pull request #1 from parapore/add-feature
| |
| |     README.mdに新機能を追加。
| |
| * commit 20ce4de3740ae2a249616403a39af55cfe9e7b65 (origin/add-feature, add-feature)
| | Author: parapore <eriglai577@gmail.com>
| | Date:   Sun Jan 7 10:45:43 2024 +0900
| |
| |     fix bug add-feature
| |
:
* commit 74c32143cae0adf61217ed4ba9d351f70e74ff28 (HEAD -> main, origin/main, origin/HEAD)
| Author: parapore <53287775+parapore@users.noreply.github.com>
| Date:   Sun Jan 7 11:35:14 2024 +0900
|
|     edit on github
|
*   commit a884db8ba5078dc0134307e96cd12f450f1857ea
|\  Merge: c2ab11f 20ce4de
| | Author: parapore <53287775+parapore@users.noreply.github.com>
| | Date:   Sun Jan 7 10:54:02 2024 +0900
| |
| |     Merge pull request #1 from parapore/add-feature
| |
| |     README.mdに新機能を追加。
| |
| * commit 20ce4de3740ae2a249616403a39af55cfe9e7b65 (origin/add-feature, add-feature)
| | Author: parapore <eriglai577@gmail.com>
| | Date:   Sun Jan 7 10:45:43 2024 +0900
| |
| |     fix bug add-feature
| |

$ git log --graph --oneline
* 74c3214 (HEAD -> main, origin/main, origin/HEAD) edit on github
*   a884db8 Merge pull request #1 from parapore/add-feature
|\
| * 20ce4de (origin/add-feature, add-feature) fix bug add-feature
| * c2e2d51 add feature README
|/
* c2ab11f Initial commit

git branch

-a:追跡ブランチ含め全て表示

-vv:詳細表示。上流ブランチも表示(ローカルブランチに対応するリモートブランチのこと)

$ git branch
* main

$ git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main

$ git branch -vv
* main 74c3214 [origin/main] edit on github //ローカルmainの上流ブランチはリモートmain

参照系

git ls-files //ステージングエリアを参照
git diff //ワークツリーとステージングエリアの差分
git diff --cached //スージングエリアとローカルリポジトリの差分
git diff HEAD //ワークツリーとローカルリポジトリの差分
git diff //ハッシュ値1 ハッシュ値2 //過去にコミットした履歴同士の差分
git log //コミット履歴の確認
git log --stat //統計情報の確認
git status //addやcommitされてない変更がないか?バージョン管理対象外ファイルがないか?を調べる。

その他

git add ファイル名
git commit -m "コミットメッセージ"
git rm --cached //ファイル名 ステージングエリアのファイル削除
git rm -r --cached //ディレクトリ名 ステージングエリアのディレクトリ削除
git mv 旧ファイル名 新ファイル名 //普通にファイル名を変更すると、バージョン管理対象外になってしまう。
git mv ファイル名 ディレクトリ名 //ファイルを別のディレクトリに移動
git merge ブランチ名
git checkout -b ブランチ名 //ブランチを新規作成して、新規ブランチにHEADを移動
git checkout ハッシュ値 //過去のコミット履歴に移動
git branch -d ブランチ名 //ブランチ削除。実際には削除せず、ポインタのみ削除。コミットは残る。