2016-06-17 8 views
6

これは慣用的なReactではないことがわかっていますが、レンダリングされる前とレンダリングされた後の両方で下流の仮想DOMの変更を通知する必要があるReactコンポーネント(逆スクロールビュー)時々リアクト私は承知しているBubbling componentWillUpdateとcomponentDidUpdate

Begin log 

render 

rendered small 

rendered small 

rendered small 

before update 

rendered big 

after update 

before update 

rendered big 

after update 

before update 

rendered big 

after update 

https://jsfiddle.net/7hwtxdap/2/

私の目標は、のようなログを見ていることです。

これは私が出発点とJSFiddleを作ったので、説明するの少し難しいですDOMの変更が一括して行われます(つまり、ログイベントはbefore update; rendered big; rendered big; after update; ...になる可能性があります)。重要な点は、DOMが変更される前後に実際に通知されることです。


、ここで行ったように私は手動で、コールバックを指定することで動作を近似することができる:https://jsfiddle.net/7hwtxdap/4/。しかし、このアプローチは規模が拡大しません。たとえば、子どもではなく子孫からのイベントを吹き飛ばすことが必要です。この種のイベントハンドラをに追加する必要はありません。すべてコンポーネントを使用します。


ユースケース:私は新しい要素が追加されたり、既存の要素のサイズを変更すると、スクロール変更の根拠からであり、(メッセージのために「逆スクロールコンポーネント」を作ることに取り組んでいます

<MyElement />クラスに似ていますが、高さが可変で、ajaxからのデータなど)、動的に変更された子を持つすべてのメッセージングアプリケーションではなく、コンテンツの下端です。これらは、Fluxのような状態ストアからデータを取得するのではなく、pub-subデータ同期モデルを使用しています(setTimeout()とかなりよく似ています)。

逆スクロール作業を行うには、あなたがこのような何か実行する必要があります。この設計に重要なこと

anyChildrenOfComponentWillUpdate() { 
    let m = this.refs.x; 
    this.scrollBottom = m.scrollHeight - m.scrollTop - m.offsetHeight; 
} 
anyChildrenOfComponentDidUpdate() { 
    let m = this.refs.x; 
    m.scrollTop = m.scrollHeight - m.offsetHeight - this.scrollBottom; 
} 

を、私は「スクロール反転されていない他の要素の周りの要素をラップできるようにしたいのですが(すなわち、逆スクロールハンドラに変更したことを通知する特別なコードを持つ必要はありません)。

+0

何をしたいコンポーネントを飾りますか? – Pcriulan

+0

これらはまた、親に「バブル」しません。 –

+0

どのように状態を管理していますか?あなたはレフィックスなどを使っていますか?また、なぜ正確にあなたは子供たちが更新するかを知る必要がありますか?あなたがその情報で何をするのか説明しておけば助けになるかもしれません。それを処理する別の方法があるかもしれません。 – jaybee

答えて

1

なぜこの機能が必要なのかわからないと、問題に対する最良の解決策がわかりません。しかし、私はこの特定の点に対処します:

私は、例えば子どもよりむしろ子孫の出来事を吹き飛ばす必要があります。私が使用するすべてのコンポーネントにこれらの種類のイベントハンドラを追加する必要はありません。

私はこのためにあなたのソリューションに2つの変更を提案します。最初はcontextを使用することを検討することですが、そうすることを決定する前に警告を必ず読んでください。これにより、コールバックを単一のコンポーネントで定義して、すべての子孫ですぐに使用できるようになります。

第2の変更点は、すべてのロギングロジックを別のコンポーネントに移動し、必要に応じて他のコンポーネントに継承させることです。このコンポーネントは、次のようになります。

このように、論理を複製せずにこの機能を新しいコンポーネントに簡単に追加できます。デモを使用してjsfiddleを更新しました。

+0

Jamesに感謝します。すべての下流のクラスを特定のクラスから継承させることは依然として偉大ではありませんが、拡張クラスのコンテキストを使用することがこれを行う最もクリーンな方法です。上記の質問をより具体的な文脈で更新し、それが役立つかどうかを確認しました。 –

3

構成上、setState呼び出しがthatコンポーネントの再レンダリングをトリガする必要があるということを、設計上、実際のところ考えてください。私がコンポーネントのライフサイクルに繋ぐために知っている最も信頼できる方法は、コンポーネントそのものです。

notifier()コールバックを小道具として渡してから、子供用コンポーネントのライフサイクルフック componentWillMount内でこの関数を呼び出しますか?

Here's a working js-fiddleです。何が起こったのか教えてください。

編集1

あなたではなく、すべての単一の子コンポーネントを介して前後にコールバックを渡したくない場合は、私はあなただけReduxFluxの特定のユースケースに遭遇してきたと思います。

Reduxを使用すると、コンポーネントはリデューサー機能を使用して、ストアの状態を変更できます。そのストアの変更を監視するすべてのコンポーネントが再レンダリングされます。Fluxは同じ考え方を使用します。

私はreduxを使用して状態設定を処理することをお勧めします。開始するには、reduxの作成者、Dan Abramovによってthese tutorial videosに行くことができます。

+0

こんにちはセント、私は実際には2番目のJSFiddleでこのような何かをしたことがわかります - どこでもコールバックを避けるためにしよう! –

+0

あなたは正しい@AaronYodaiken私はあなたの特定の好みを取るために私の答えを編集しました。 Redux/Fluxは私の友人に行く方法です:) – Cent

+0

Cent、reduxは素晴らしいですが、ここではそれは正しい解決策ではありません - 私のスクロールコンテナは、内部のものがどのパスに依存しているかを知る必要はありません。カプセル化を壊す!) –

0

react-virtualizedには逆スクロールリストの例があるかもしれません。

export default class Example extends Component { 
    constructor (props) { 
    super(props) 

    this.state = { 
     list: [] 
    } 
    } 

    componentDidMount() { 
    this._interval = setInterval(::this._updateFeed, 500) 
    } 

    componentWillUnmount() { 
    clearInterval(this._interval) 
    } 

    render() { 
    const { list } = this.state 

    return (
     <div className={styles.VirtualScrollExample}> 
     <VirtualScroll 
      ref='VirtualScroll' 
      className={styles.VirtualScroll} 
      width={300} 
      height={200} 
      rowHeight={60} 
      rowCount={list.length} 
      rowRenderer={::this._rowRenderer} 
     /> 
     </div> 
    ) 
    } 

    _updateFeed() { 
    const list = [ ...this.state.list ] 

    list.unshift(
     // Add new item here 
    ) 

    this.setState({ list }) 

    // If you want to scroll to the top you can do it like this 
    this.refs.VirtualScroll.scrollToRow(0) 
    } 

    _rowRenderer (index) { 
    return (
     // Your markup goes here 
    ) 
    } 
} 
+0

反応仮想化は静的な 'rowHeight'に依存しています。つまり、DOMの変更をリッスンする必要はありません。 –

1

ジェームズは、あなたがすべての子孫コンポーネントにダウン渡すことができますコールバック関数を定義するためにコンテキストを使用することができますし、あなたのコンポーネントを拡張したくない場合は、あなたがにデコレータを使用することができ、言ったようにそれらの周りに高次のコンポーネントをラップします。

あなたの容器に:

class Container extends React.Component { 
    static childContextTypes = { 
     log: React.PropTypes.func.isRequired 
    }; 

    getChildContext() { 
     return { 
      log: (msg) => { 
       console.log(msg) 
      } 
     } 
    } 

    render() { 
    console.log("Begin Log") 
    return (
     <div> 
      <MyElement /> 
      <MyElement /> 
      <MyElement /> 
     </div> 
    ); 
    } 
} 

デコレータクラス:このような何か

export const Logger = ComposedComponent => class extends React.Component { 
    static contextTypes = { 
     log: React.PropTypes.func.isRequired 
    }; 

    constructor(props) { 
     super(props) 
    } 

    componentWillReceiveProps(nextProps) { 
     this.context.log('will receive props'); 
    } 

    componentWillUpdate() { 
     this.context.log('before update'); 
    } 

    componentDidUpdate() { 
     this.context.log('after update'); 
    } 

    render() { 
     return <ComposedComponent {...this.props}/> 
    } 
} 

は、その後、あなたがcomponentWillMountとcomponentDidUpdateライフサイクルメソッドについて

@Logger 
class MyElement extends React.Component { 
... 
} 
+0

こんにちは私は、 'ComposedComponent'はラップされた/デコレータコンポーネントではなく、更新イベントを受け取っているので、これがうまくいくとは思わない。 –

関連する問題