web-dev-qa-db-ja.com

Redux接続コンポーネントは、いつ再レンダリングするかをどのように知るのですか?

私はおそらく非常に明白な何かを見逃しているので、自分自身をクリアしたいと思います。

これが私の理解です。
単純な反応コンポーネントには、statespropsがあります。 statesetStateを更新すると、コンポーネント全体が再レンダリングされます。 propsはほとんど読み取り専用であり、更新しても意味がありません。

store.subscribe(render)のようなものを介してreduxストアにサブスクライブする反応コンポーネントでは、ストアが更新されるたびに明らかに再レンダリングされます。

react-redux には、状態ツリーの一部(コンポーネントにとって重要なもの)とactionCreatorsをコンポーネントにpropsとして注入するヘルパーconnect()があり、通常は

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

しかし、setStateTodoListComponentがredux状態ツリーの変更(再レンダリング)に反応するために不可欠であるという理解では、stateまたはsetStateを見つけることができません。 TodoListコンポーネントファイルの関連コード。次のように表示されます。

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

誰かが私が欠けているものについて正しい方向に私を指すことができますか?

追伸: redux package にバンドルされているtodoリストの例に従っています。

56
Joe Lewis

connect関数は、ストアにサブスクライブするラッパーコンポーネントを生成します。アクションがディスパッチされると、ラッパーコンポーネントのコールバックが通知されます。次に、mapState関数を実行し、shallow-compares今回の結果オブジェクトと前回の結果オブジェクトを比較します(つまり、同じ値でreduxストアフィールドをrewriteすると、再レンダリングはトリガーされません)。結果が異なる場合、結果を小道具として「実際の」コンポーネントに渡します。

Dan Abramovは( connect.js )でconnectの非常に単純化されたバージョンを作成しました。 Redux performance に関するいくつかの記事へのリンクもあります。これらの記事では、関連するいくつかのアイデアについて説明しています。

update

React-Redux v6.0. 接続されたコンポーネントがストアからデータを受信する方法にいくつかの大きな内部変更を加えました。

その一環として、connect AP​​Iとその内部構造がどのように機能し、それらがどのように変化してきたかを説明する投稿を書きました。

慣用的Redux:React-Reduxの歴史と実装

78
markerikson

私の答えは、左のフィールドから少し外れています。この記事に私を導いた問題に光を当てます。私の場合、新しい小道具を受け取ったにもかかわらず、アプリは再レンダリングされていないようでした。
React開発者は、このよくある質問に対して、(ストア)が変更された場合、99%の時間でリアクションが再表示されない理由についての質問に答えました。しかし、他の1%については何もありません。突然変異はここではありませんでした。


TLDR;

componentWillReceivePropsは、stateを新しいpropsと同期させる方法です。

エッジケース:stateが更新されると、thenアプリがdoes再レンダリングされます!


アプリがstateのみを使用して要素を表示している場合、propsは更新できますが、stateは更新されないため、 -render。

Redux stateから受信したpropsに依存するstoreがありました。必要なデータはまだストアにないので、componentDidMountから適切に取得しました。コンポーネントがmapStateToPropsを介して接続されているため、レデューサーがストアを更新したときに小道具を取り戻しました。しかし、ページはレンダリングされず、状態はまだ空の文字列でいっぱいでした。

これの例は、ユーザーが保存されたURLから「投稿の編集」ページをロードしたことです。 URLからpostIdにアクセスできますが、情報はまだstoreにないため、フェッチします。ページ上のアイテムは制御されたコンポーネントです。したがって、表示しているすべてのデータはstateにあります。

Reduxを使用して、データがフェッチされ、ストアが更新され、コンポーネントはconnectedになりましたが、アプリは変更を反映しませんでした。よく見ると、propsは受信されましたが、アプリは更新されませんでした。 stateは更新されませんでした。

さて、propsは更新および伝播されますが、stateは更新されません。 stateに更新を指示する必要があります。

render()でこれを行うことはできず、componentDidMountはすでにサイクルを終了しています。

componentWillReceivePropsは、変更されたstate値に依存するpropプロパティを更新する場所です。

使用例:

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

この記事に、他の多数の投稿、ブログ、リポジトリが言及できなかった解決策を教えてくれた叫び声を上げなければなりません。この明らかに不明瞭な問題に対する答えを見つけるのに苦労した他の誰もが、ここにあります:

ReactJsコンポーネントのライフサイクルメソッド—詳細な説明

componentWillReceivePropsは、state更新と同期を保つためにpropsを更新する場所です。

stateが更新されると、thenフィールドはstatedoに応じて再レンダリングされます!

8
SherylHohman

この回答は、Brian Vaughnの記事 おそらく派生国家は必要ない (2018年6月7日)の要約です。

小道具から状態を導出することは、あらゆる形態のアンチパターンです。古いcomponentWillReceivePropsと新しいgetDerivedStateFromPropsの使用を含む。

小道具から状態を導出する代わりに、次の解決策を検討してください。

2つのベストプラクティスの推奨事項

function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

何らかの理由で推奨事項があなたの状況に合わない場合、2つの選択肢があります。

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}
0
Mowzer

私はreduxだけが知っているように、ストアの状態が変化すると、コンポーネントが変更された状態に依存している場合にcomponentWillRecievePropsを呼び出し、コンポーネントを強制的に更新する必要があります

1-store state change-2-call(componentWillRecieveProps(()=> {3-component state change}))

0