15

私は、HOC を使用して再利用可能な React トランジションを作成するための Chang Wang のチュートリアルReactTransitionGroup(パート 1 パート 2 ) と、ページ遷移に関する Huan Ji のチュートリアル (リンク) に従っています。

私が直面している問題は、React.cloneElement更新された小道具をその子の 1 つに渡していないように見えるのに対し、他の子は更新された小道具を適切に受け取ることです。

まず、いくつかのコード:

TransitionContainer.js

TransitionContainerAppは、 Huan Ji のチュートリアルに似たコンテナー コンポーネントです。状態のスライスを子に注入します。

の子TransitionGroupはすべて、呼び出された HOC のインスタンスですTransition(コードはさらに下にあります) 。

import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
class TransitionContainer extends React.Component{
  render(){
    console.log(this.props.transitionState);
    console.log("transitionContainer");
    return(
      <div>
      <TransitionGroup>
      {
        React.Children.map(this.props.children,
         (child) => React.cloneElement(child,      //These children are all instances of the Transition HOC
           { key: child.props.route.path + "//" + child.type.displayName,
             dispatch: this.props.dispatch,
             transitionState: this.props.transitionState
           }
         )
        )
      }

      </TransitionGroup>
      </div>
    )
  }
}
export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)

Transition.js

TransitionChang Wang の HOC に似ています。componentWillEnterいくつかのオプションを取り、 +フックを定義しcomponentWillLeave、コンポーネントをラップします。TransitionContainer(上)props.transitionStateこの HOC に注入します。ただし、状態が変化しても小道具が更新されないことがあります (以下の問題を参照) 。

import React from 'react';
import getDisplayName from 'react-display-name';
import merge from 'lodash/merge'
import classnames from 'classnames'
import * as actions from './actions/transitions'
export function transition(WrappedComponent, options) {
  return class Transition extends React.Component {
    static displayName = `Transition(${getDisplayName(WrappedComponent)})`;
    constructor(props) {
      super(props);
      this.state = {
          willLeave:false,
          willEnter:false,
          key: options.key
      };
    }
    componentWillMount(){
      this.props.dispatch(actions.registerComponent(this.state.key))
    }
    componentWillUnmount(){
      this.props.dispatch(actions.destroyComponent(this.state.key))
    }
    resetState(){
      this.setState(merge(this.state,{
        willLeave: false,
        willEnter: false
      }));
    }
    doTransition(callback,optionSlice,willLeave,willEnter){
      let {transitionState,dispatch} = this.props;
      if(optionSlice.transitionBegin){
        optionSlice.transitionBegin(transitionState,dispatch)
      }
      if(willLeave){
        dispatch(actions.willLeave(this.state.key))
      }
      else if(willEnter){
        dispatch(actions.willEnter(this.state.key))
      }
      this.setState(merge(this.state,{
        willLeave: willLeave,
        willEnter: willEnter
      }));
      setTimeout(()=>{
        if(optionSlice.transitionComplete){
          optionSlice.transitionEnd(transitionState,dispatch);
        }
        dispatch(actions.transitionComplete(this.state.key))
        this.resetState();
        callback();
      },optionSlice.duration);
    }
    componentWillLeave(callback){
      this.doTransition(callback,options.willLeave,true,false)
    }
    componentWillEnter(callback){
      this.doTransition(callback,options.willEnter,false,true)
    }
    render() {

      console.log(this.props.transitionState);
      console.log(this.state.key);

      var willEnterClasses = options.willEnter.classNames
      var willLeaveClasses = options.willLeave.classNames
      var classes = classnames(
        {[willEnterClasses] : this.state.willEnter},
        {[willLeaveClasses] : this.state.willLeave},
      )
      return <WrappedComponent animationClasses={classes} {...this.props}/>
    }
  }
}

オプション

オプションの構造は次のとおりです。

{
  willEnter:{
    classNames : "a b c",
    duration: 1000,
    transitionBegin: (state,dispatch) => {//some custom logic.},
    transitionEnd: (state,dispatch) => {//some custom logic.}
         // I currently am not passing anything here, but I hope to make this a library
         // and am adding the feature to cover any use case that may require it.

  },
  willLeave:{
    classNames : "a b c",
    duration: 1000,
    transitionBegin: (state,dispatch) => {//some custom logic.},
    transitionEnd: (state,dispatch) => {//some custom logic.}

  }
}

移行ライフサイクル (onEnter または onLeave)

  • コンポーネントが実装されると、actions.registerComponent発送され ます
    • componentWillMount
  • コンポーネントのcomponentWillLeaveまたはcomponentWillEnterフックが呼び出されると、オプションの対応するスライスがに送信されますdoTransition
  • doTransition:
    • ユーザー提供の transitionBegin 関数が呼び出されます ( optionSlice.transitionBegin)
    • デフォルトaction.willLeaveまたはaction.willEnter発送されます
    • アニメーションの継続時間にはタイムアウトが設定されています ( optionSlice.duration)。タイムアウトが完了すると:
      • ユーザー提供の transitionEnd 関数が呼び出されます ( optionSlice.transitionEnd)
      • デフォルトactions.transitionCompleteは発送されます

基本的に、optionSlice は、ユーザーがいくつかのオプションを渡すことを許可するだけです。optionSlice.transitionBeginユースケースに適してoptionSlice.transitionEndいる場合は、アニメーションの進行中に実行されるオプションの関数にすぎません。現在、コンポーネント用に何も渡していませんが、これをすぐにライブラリにしたいと考えているので、ベースをカバーしているだけです。

とにかく遷移状態を追跡するのはなぜですか?

ここに画像の説明を入力

入る要素に応じて、出るアニメーションが変化し、その逆も同様です。

たとえば、上の画像では、青が入ると赤が右に移動し、青が出ると赤が左に移動します。ただし、グリーンが入ると赤は左に移動し、グリーンが出ると赤は右に移動します。これを制御するには、現在の遷移の状態を知る必要があります。

問題:

TransitionGroupは 2 つの要素が含まれています。1 つは入り、もう 1 つは出ます (react-router によって制御されます)。呼び出された小道具transitionStateをその子に渡します。TransitionHOC ( の子)TransitionGroupは、アニメーションの過程で特定の redux アクションをディスパッチします。入るコンポーネントは propsのTransition変更を期待どおりに受け取りますが、出るコンポーネントは凍結されます。小道具は変わりません。

更新された小道具を受け取らないのは、常に終了しているものです。ラップされたコンポーネントの切り替え (出入り) を試みましたが、問題はラップされたコンポーネントが原因ではありません。

画像

画面遷移: 遷移

React DOM での遷移

トランジション2

この場合、既存のコンポーネント Transition(Connect(Home))) は、更新された props を受け取っていません。

これが事実である理由はありますか?すべての助けを前もって感謝します。

更新 1:

import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
var childFactoryMaker = (transitionState,dispatch) => (child) => {
  console.log(child)
  return React.cloneElement(child, {
    key: (child.props.route.path + "//" + child.type.displayName),
    transitionState: transitionState,
    dispatch: dispatch
  })
}

class TransitionContainer extends React.Component{
  render(){
    let{
      transitionState,
      dispatch,
      children
    } = this.props
    return(
      <div>
      <TransitionGroup childFactory={childFactoryMaker(transitionState,dispatch)}>
          {
            children
          }
      </TransitionGroup>
      </div>
    )
  }
}

export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)

私はTransitionContainer上記に私のものを更新しました。現在、フックcomponentWillEntercomponentWillLeaveフックは呼び出されていません。関数にログを記録するReact.cloneElement(child, {...})childFactory、フック (および のような定義済み関数doTransition) がprototype属性に存在します。constructorcomponentWillMountおよびのみcomponentWillUnmountが呼び出されます。これは、keyプロップが を介して注入されていないためだと思われますReact.cloneElement。注射されてるけどtransitionStatedispatch

更新 2:

import React from 'react';
import TransitionGroup from 'react-addons-transition-group';
import {connect} from 'react-redux';
var childFactoryMaker = (transitionState,dispatch) => (child) => {
  console.log(React.cloneElement(child, {
    transitionState: transitionState,
    dispatch: dispatch
  }));
  return React.cloneElement(child, {
    key: (child.props.route.path + "//" + child.type.displayName),
    transitionState: transitionState,
    dispatch: dispatch
  })
}

class TransitionContainer extends React.Component{
  render(){
    let{
      transitionState,
      dispatch,
      children
    } = this.props
    return(
      <div>
      <TransitionGroup childFactory={childFactoryMaker(transitionState,dispatch)}>
      {
        React.Children.map(this.props.children,
            (child) => React.cloneElement(child,      //These children are all instances of the Transition HOC
                { key: child.props.route.path + "//" + child.type.displayName}
            )
        )
      }
      </TransitionGroup>
      </div>
    )
  }
}

export default connect((state)=>({transitionState:state.transitions}),(dispatch)=>({dispatch:dispatch}))(TransitionContainer)

TransitionGroup ソースをさらに調べたところ、キーを間違った場所に置いていることに気付きました。今はすべて順調です。助けてくれてどうもありがとう!!

4

1 に答える 1