概要
子コンポーネントをクリックした際に他方のコンポーネント(兄弟関係)の状態を変更するような実装を行いたいと考えています。
前提
React初学レベルです。
Hooksを用いてSPAを実装しようと勉強中の段階です。
行いたいこと
複数の兄弟関係の子コンポーネントがありそれらのいずれかのコンポーネントをクリックした際に、すべての兄弟関係のコンポーネントにクラスを付与したい。
その際、子コンポーネントに渡されたprops
の値に応じて付与するクラスを変更したい。
実装内容
true
, false
をランダムにprops
で渡し(以下例では便宜上ランダムには渡していません)、子コンポーネントのいずれかをクリックした際に、propsに応じて全ての子コンポーネントに処理を行うような実装です。
具体的にはクリックすると全ての子コンポーネントにprops
のtrue/false
に応じたクラスを付与するというものです。
javascript
1// Parent.jsx 2 3const Parent = () => { 4 const arrayBool = [true, false, false] 5 6 return ( 7 <ul> 8 {arrayBool.map((bool, i) => { 9 return <Child key={i.toString()} bool={bool}/> 10 })} 11 </ul> 12 ) 13 14} 15 16export default Parent
javascript
1// Child.jsx 2 3const Child = (props) =>{ 4 const hoge = () => { 5 if(props.bool) { 6 // trueの処理 7 } else { 8 // falseの処理 9 } 10 } 11 12 return ( 13 <li> 14 <button onClick={() => hoge()}> 15 <span>Click!</span> 16 </button> 17 </li> 18 ) 19} 20 21export default Child
行ったこと
- 上記例では当然一つの子コンポーネントをクリックしても当該コンポーネントのクリックイベントしか実行されません。
- また子コンポーネントのイベントで他の子コンポーネントのイベントハンドラを実行することができませんでした(そういった実装自体がReactの設計に反しているとも思います)ので、以下のように実装しようと試みました。
- 子コンポーネントがクリックされたことを親に伝える(子のクリックイベントで親の
state
を変更する など) useRef
を用いて親から全ての子コンポーネントの関数を実行する- 子コンポーネントに渡っている
props
に応じたクラスを付与する
javascript
1// Parent.jsx 2 3const Parent = () => { 4 const [value, setValue] = useState('') 5 6 const childRef01 = useRef(); 7 const childRef02 = useRef(); 8 const childRef03 = useRef(); 9 10 // valueが変更されたら子コンポーネントの関数を実行する 11 useEffect(() => { 12 childRef01.current.childFunc(); 13 childRef02.current.childFunc(); 14 childRef03.current.childFunc(); 15 }, [value]); 16 17 const changeValue = (text) => { 18 setValue(text) 19 } 20 21 return ( 22 <> 23 <Child id={true} ref={childRef01} text={'HOGE'} event={changeValue} bool={true} /> 24 <Child id={false} ref={childRef02} text={'FUGA'} event={changeValue} bool={false} /> 25 <Child id={true} ref={childRef03} text={'PIYO'} event={changeValue} bool={false} /> 26 </> 27 ); 28}; 29 30export default Parent
javascript
1// Child.jsx 2 3import React, { forwardRef, useImperativeHandle, useState } from 'react'; 4 5const ChildrenBase = (props, ref) => { 6 const [bool, setBool] = useState(''); 7 const [className, setClassName] = useState('') 8 9 // 親から呼ばれる関数を定義 10 useImperativeHandle(ref, () => ({ 11 childFunc() { 12 if(props.bool) { 13 // trueの処理 14 } else { 15 // falseの処理 16 } 17 }, 18 })); 19 20 return ( 21 <p className={className} onClick={() => props.event('props.text')}>{props.text}</p> 22 ); 23}; 24const Child = forwardRef(ChildrenBase); 25 26export default Child
上記手順で一つの子コンポーネントをクリックした際に全ての子コンポーネントに定義されている関数を実行することはできたのですが、childFunc()
内で子コンポーネントにクラスをadd
する処理が記述できずに断念しました。
(そもそも「子コンポーネントが親コンポーネントの関数を実行する」という実装自体に疑問があるため、無理矢理な実装であることは理解しています。)
そのほかにもこちらの記事を参考に兄弟間でイベントを実行し合うような実装も試みましたが、望んだ実装はできませんでした。
既述した「行いたいこと」のような挙動を実装するにはどのようにしたらよいでしょうか。
よろしくお願いいたします。
###マシン環境
MacBook Air (M1, 2020) Big Sur v11.3.1
node: v16.2.0
npm: v7.13.0
yarn: v1.22.10