【React】コンポーネントを再利用できるようにするリファクタリング
例えばこのような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' }