No.
2022-01-08
  • Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec
  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

やったこと

ハッカソンの準備

useContextでのエラー

  <ApplyUseLanguageContext.Provider value={applyUseLanguage} >
  </ApplyUseLanguageContext.Provider>

上のように単体でContextに値を入れた場合、ずっとオブジェクトとして渡されると思っていた

そのため、下のように

	const { applyStartDate } = useContext(ApplyStartDateContext)

オブジェクトとして受け取っていたらずっと ~~ does not exists type ~~のエラーが出ていた
正しい回答としては、

	const applyStartDate = useContext(ApplyStartDateContext)

である。

constateについて

contextを分離させるために、頑張った結果が以下の写真だ
スクリーンショット 2022-01-08 13 39 51
実装としては正解らしい。。。が、みてられないくらい量が多い   これを解決してくれるのがconstateである。
github source code

使用方法

$ npm install constate --save
# or
$ yarn add constate

まずはinstallから

設置

import { useState } from 'react';
import constate from 'constate';
const [CountProvider, useCountContext] = constate(() => {
  const [count] = useState(0);
  return count;
});

constateを使った結果

export const [RecruteTitleProvider, useRecruteTitleContext, useSetRecruteTitleContext] = constate(() => {
  const [recruteTitle, setRecruteTitle] =
    useState<string | undefined>(undefined);
  return { recruteTitle, setRecruteTitle };
},
  value => value.recruteTitle,
  value => value.setRecruteTitle
);
export const [RecruteUseLanguageProvider, useRecruteUseLanguageContext, useSetRecruteUseLanguageContext] =
  constate(() => {
    const [recruteUseLanguage, setRecruteUseLanguage] =
      useState<string | undefined>(undefined);
    return { recruteUseLanguage, setRecruteUseLanguage };
  },
    value => value.recruteUseLanguage,
    value => value.setRecruteUseLanguage
  );
export const [RecruteDateProvider, useRecruteStartDateContext, useSetStartRecruteDateContext, useEndRecruteDateContext, useSetEndRecruteDateContext] = constate(() => {
	const [recruteStartDate, setRecruteStartDate] = useState<Date | null | undefined>(null)
	const [recruteEndDate, setRecruteEndDate] = useState<Date | null | undefined>(null)
	return { recruteStartDate, setRecruteStartDate, recruteEndDate, setRecruteEndDate }
},
  value => value.recruteStartDate,
  value => value.setRecruteStartDate,
  value => value.recruteEndDate,
  value => value.setRecruteEndDate,
)
export const RecrutePostProvider = (props: { children: ReactNode }) => {
  const { children } = props;
  return (
    <RecruteTitleProvider>
      <RecruteUseLanguageProvider>
	<RecruteDateProvider>
	  {children}
	</RecruteDateProvider>
      </RecruteUseLanguageProvider>
    </RecruteTitleProvider>
  );
};

こうなる
しかし、実際にContextを使うと依存関係が増えるためなるべく使わない設計にするべき

簡潔なまとめ

上のだとちょっとみずらいので簡潔なパターンを掲示

export const [countProvider, useCountContext, useSetCountContext] = constate(() => {
  const [count, setCount] =
    useState<number>(0);
  return { count, setCount };
  },
  value => value.count,
  value => value.setCount
);
  • constateの第二引数以降に、第一引数の関数の返り値をセレクターで分割することで、内部でContext分割してくれる
  • この場合だとcountProviderは2つのProviderを内包するReactNodeになる
  • 残りの二つのHookはそれぞれのConsumerになる

ReactNodeとは

Reactのコンポーネント周りの用語を整理する

type ReactNode =
  | ReactChild
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined

という定義になっている
ReactNodeが使われるのは、createElementの引数

function createElement<P extends HTMLAttributes<T>, T extends HTMLElement>(
  type: keyof ReactHTML,
  props?: (ClassAttributes<T> & P) | null,
  ...children: ReactNode[]
): DetailedReactHTMLElement<P, T>

ReactChild

ReactNodeに含まれるReactChildは

type ReactChild = ReactElement | ReactText
  • props.childrenには直接関係はない
  • ReactChild は ReactNode の型定義で使われているだけ

    ReactText

    type ReactText = string | number
    
  • primitive なものを 2 つ組み合わせただけのもの これによって、以下のようにコンポーネントのchildrenにprimitiveなものも含むことができるようになる
    const Hoge = () => {
    return <div>1</div>
    }
    

参考資料