私は、HOC を使用して再利用可能な React トランジションを作成するための Chang Wang のチュートリアルReactTransitionGroup
(パート 1 パート 2 ) と、ページ遷移に関する Huan Ji のチュートリアル (リンク) に従っています。
私が直面している問題は、React.cloneElement
更新された小道具をその子の 1 つに渡していないように見えるのに対し、他の子は更新された小道具を適切に受け取ることです。
まず、いくつかのコード:
TransitionContainer.js
TransitionContainer
App
は、 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
Transition
Chang 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
は発送されます
- ユーザー提供の transitionEnd 関数が呼び出されます (
- ユーザー提供の transitionBegin 関数が呼び出されます (
基本的に、optionSlice は、ユーザーがいくつかのオプションを渡すことを許可するだけです。optionSlice.transitionBegin
ユースケースに適してoptionSlice.transitionEnd
いる場合は、アニメーションの進行中に実行されるオプションの関数にすぎません。現在、コンポーネント用に何も渡していませんが、これをすぐにライブラリにしたいと考えているので、ベースをカバーしているだけです。
とにかく遷移状態を追跡するのはなぜですか?
入る要素に応じて、出るアニメーションが変化し、その逆も同様です。
たとえば、上の画像では、青が入ると赤が右に移動し、青が出ると赤が左に移動します。ただし、グリーンが入ると赤は左に移動し、グリーンが出ると赤は右に移動します。これを制御するには、現在の遷移の状態を知る必要があります。
問題:
にTransitionGroup
は 2 つの要素が含まれています。1 つは入り、もう 1 つは出ます (react-router によって制御されます)。呼び出された小道具transitionState
をその子に渡します。Transition
HOC ( の子)TransitionGroup
は、アニメーションの過程で特定の redux アクションをディスパッチします。入るコンポーネントは propsのTransition
変更を期待どおりに受け取りますが、出るコンポーネントは凍結されます。小道具は変わりません。
更新された小道具を受け取らないのは、常に終了しているものです。ラップされたコンポーネントの切り替え (出入り) を試みましたが、問題はラップされたコンポーネントが原因ではありません。
画像
React DOM での遷移
この場合、既存のコンポーネント 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
上記に私のものを更新しました。現在、フックcomponentWillEnter
とcomponentWillLeave
フックは呼び出されていません。関数にログを記録するReact.cloneElement(child, {...})
とchildFactory
、フック (および のような定義済み関数doTransition
) がprototype
属性に存在します。constructor
、componentWillMount
およびのみcomponentWillUnmount
が呼び出されます。これは、key
プロップが を介して注入されていないためだと思われますReact.cloneElement
。注射されてるけどtransitionState
。dispatch
更新 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 ソースをさらに調べたところ、キーを間違った場所に置いていることに気付きました。今はすべて順調です。助けてくれてどうもありがとう!!