LoginSignup
1
0

More than 1 year has passed since last update.

Reactで各コンポ―ネントで実施している共通処理をCompositionで外だししてしまう

Posted at

Reactでコンポーネントをたくさん作っていると各コンポーネントに共通処理が出てきてうっとおしい場合がありませんか?

例)初回レンダリング時にログを出力する
例)特定の属性の値(valueとする)が設定されているかをrequired属性の有無によってチェックするかを切り替える

オブジェクト指向だとこういった共通する処理は親クラスで実装し、親を継承した子クラスに独自の処理を記載するということが行われます。

ReactではComposition機能が用意されているのでそれを使って実装したいと思います。

・CustomComponentAには1つの入力があり、required付加時には未入力の場合は必須エラー
・CustomComponentBには2つの入力があり、required付加時にはどちらも未入力の場合は必須エラー

App.tsxで以下を呼び出してます。
CustomComponentA 必須
CustomComponentB 必須
CustomComponentB 任意

App.tsx
import { useState } from 'react'
import CustomComponentA from './CustomComponentA'
import CustomComponentB from './CustomComponentB'

function App() {
  const [value,setValue] = useState<string>('')
  const [address,setAddress] = useState<string>();

  const onChange = (e:ChangeEvent<HTMLInputElement>) =>{
    setValue(e.target.value)
  }

  return (
    <div className="App">
      <CustomComponentA required/>
      <CustomComponentB required/>
      <CustomComponentB />
    </div>
  )
}

export default App

共通処理を記述するCompositionです

SampleComposition.tsx
import React, { ReactNode, useEffect, useState } from 'react'

type propsType = {
  children:ReactNode,
  childprop?:any,
  value?:string
}

const SampleComposition = (props:propsType) => {
  const [errormessage,setErrormessage] = useState('');
  const [value,setValue] = useState(props.value);

  useEffect(()=>{
    //初回時にログを出すとか・・
    console.log('LOG:I am parent.');
  },[])
  useEffect(()=>{
    setValue(props.value);
  },[props.value])

  const onBlur = () =>{
    //blur時に必須チェックするとか・・・
    if(!!props.childprop && !!props.childprop.required){
      console.log('LOG:Child is required!!');

      if(!value){
        setErrormessage('★必須入力エラー(value未設定)')
      }else{
        setErrormessage('')
      }
    }
  }
  
  return (
    <fieldset onBlur={onBlur}>
      <legend>Parent</legend>
      {props.children}
      <p>{errormessage}</p>
    </fieldset>
  )
}

export default SampleComposition;

CustomComponentAのポイントはここではrequiredの実装はしていません。
※微妙な点:
・Compositionコンポーネントと呼出しコンポーネントの結合度が高い気がする。共通化を主眼においてしかたないとするか?
・汎用性を持たせる観点からSampleCompositionのchildpropをanyにしてしまっている点でtypescriptの恩恵にあずかれない。
 ※これは共通のtypeやらinterfaceやらを定義すればよいかな?

CustomComponentA.tsx
import React, { ChangeEvent, ChangeEventHandler, useEffect, useState } from 'react'
import SampleComposition from "./SampleComposition";

const CustomComponentA = (props: any) => {
  const [value, setValue] = useState(props.value);

  useEffect(() => {
    console.log('LOG:I am CustomComponentA.');
  }, [])

  const onChange = (e:ChangeEvent<HTMLInputElement>) =>{
    setValue(e.target.value)
  }

  return (
    <>
      <SampleComposition childprop={props} value={value}>
        <input value={value} onChange={onChange}/>
      </SampleComposition>
    </>
  )
}

export default CustomComponentA;
CustomComponentB.tsx
import React, { ChangeEvent, ChangeEventHandler, useEffect, useState } from 'react'
import SampleComposition from "./SampleComposition";

const CustomComponentB = (props: any) => {
  const [valueA, setValueA] = useState('');
  const [valueB, setValueB] = useState('');

  useEffect(() => {
    console.log('LOG:I am CustomComponentB.');
  }, [])

  const onChangeA = (e:ChangeEvent<HTMLInputElement>) =>{
    setValueA(e.target.value)
  }
  const onChangeB = (e:ChangeEvent<HTMLInputElement>) =>{
    setValueB(e.target.value)
  }

  return (
    <>
      <SampleComposition childprop={props} value={valueA + valueB}>
        <input value={valueA} onChange={onChangeA}/>
        <input value={valueB} onChange={onChangeB}/>
      </SampleComposition>
    </>
  )
}

export default CustomComponentB;

実行結果
blur時に無事エラーメッセージが出力されました。
子コンポーネントにrequiredを定義せずchildpropとして渡すことでかなり力技ですが実装はできました。

image.png

入力時にはメッセージが消えていることを確認
image.png

まぁCompositionも使い方色々ですかね。

以上

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0