質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

再レンダリングを防ぐために子コンポーネントでconnectするべきか

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 222

mickey

score 27

現在、Next.jsを基盤にreact-reduxで状態管理するアプリケーションを作成しています。

以下のコードについて説明いたします。

  • Results.jsでstoreにconnectし、APIから取得済みのitemsを子コンポーネントであるListItem.jsを用いてリスト表示
  • ListItemにある保存ボタンをクリックするとコールバック関数であるhandleClickを通じてtoggleBookmarkをdispatchし、itemのプロパティーであるis_bookmarked(Boolean)を変更する
// items

[
  {id: 1, title: 'title1', is_bookmarked: true},
  {id: 2, title: 'title2', is_bookmarked: false}
]

// components/molecules/ListItem.js

const ListItem = React.memo(({ item, handleClick }) => {
  return (
    <div>
      <p>{item.title}</p>
      <button onClick={handleClick}>
        {item.is_bookmarked ? '保存済み' : '保存する'}
      </button>
    </div>
  )
})

export default ListItem

// components/organisms/Results.js

const Search = ({ items, toggleBookmark }} => {
  return items.map(item => (
    <ListItem
      key={item.id}
      item={item}
      handleClick={() => toggleBookmark(item.id)}
    />
  ))
}

const mapStateToProps = (state) => ({
  items: state.classified.items,
})

const mapDispatchToProps = (dispatch) => {
  return {
    toggleBookmark: bindActionCreators(toggleBookmark, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Results)

// reducer.js

export default function reducer(state = [], action) {
  switch (action.type) {
    case actionTypes.TOGGLE_BOOKMARK:
      return {
        ...state,
        items: state.items.map(el => el.id === action.payload ? { ...el, is_bookmarked: !el.is_bookmarked } : el)
      }
    default:
      return state
  }
}

上記は可能な限りAtomic designに沿って作成したため、molecules内にあるListItemではロジックを記述せず、上層のorganismsにあるResultsにてconnect、またロジックの記述をしています。(説明のためatomsをmoleculesにまとめて記載、またcontainerとpresentationalに分けずまとめて記載しています)

この状態でreact devtoolsにあるHighlight Updateをチェックし、任意のListItemの保存ボタンをクリックすると、クリックされたボタンのあるコンポーネントだけでなく、それ以外の全てのListItemが再レンダリングされるのが確認できます。

ここで質問です。

  1. クリックされたボタンのあるコンポーネントのみを再レンダリングしたい場合、どのような方法をとるべきなのでしょうか?
  2. 上記の実装において問題点はありますか?

例えばResultsではなくListItemでconnectした場合は、当然ですがボタン押下時に対象のコンポーネントのみが再レンダリングされました。しかしAtomic Designの観点等からconnectするのはorganisms以上のコンポーネントでするべきだと認識しているので、それは避けたいです。

親コンポーネントでconnectし、そこからstateやコールバック関数を子コンポーネントに渡す限り、上記コードにおける親に付随する全子コンポーネントの再レンダリングは免れないのでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

クリックされたボタンのあるコンポーネントのみを再レンダリングしたい場合、どのような方法をとるべきなのでしょうか?

toggleBookmark自体を<ListItem>に渡してしまえば、関数の再生成という問題はありません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/10 10:23

    ご回答ありがとうございます。
    <ListItem key={item.id} item={item} toggleBookmark={toggleBookmark} />として
    ListItemの方で、受け取ったtoggleBookmarkを使ってonClick={() => toggleBookmark(item.id)}するという理解で合っていますか?
    こちらが合っていると仮定して試してみたのですがクリックされたコンポーネント以外も再レンダリングされました。なので私が回答を理解できていないと思いますので、もう少しだけ詳しくご教授願えますでしょうか?

    キャンセル

  • 2020/07/10 10:27

    toggleBookmark自体が変化し続けている、ということはありませんか?

    (なお、onClick={() => toggleBookmark(item.id)}のようにJSX内に直接関数を書くと、毎回別インスタンスとなって再描画が必ず発生します)

    キャンセル

  • 2020/07/10 10:44

    toggleBookmarkは
    export const toggleBookmark = (id) => async dispatch => {
    return dispatch({ type: classifiedActionTypes.TOGGLE_BOOKMARK, payload: id })
    }
    タイプとitemのidを返す関数なのですが、こちらは変化し続けると捉えるべきでしょうか?

    キャンセル

  • 2020/07/11 22:20

    横から失礼します。Search に渡る (そしてそのまま ListItem に渡す) toggleBookmark は、生の toggleBookmark ではなく、mapDispatchToProps の中で bindActionCreators されたものです。で、mapDispatchToProps の結果は connect の中でメモ化されるので、Search に渡る toggleBookmark は基本的に (dispatch が変わらない限り) 変化しないはずです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る