我正在关注 Chang Wang 的使用 HOC 制作可重用 React 转换的教程和ReactTransitionGroup
(第 1 部分第 2 部分)以及 Huan Ji 的页面转换教程(链接)。
我面临的问题是React.cloneElement
似乎没有将更新的道具传递给它的一个孩子,而其他孩子确实正确地收到了更新的道具。
首先,一些代码:
过渡容器.js
TransitionContainer
是类似于App
环记教程中的容器组件。它将状态的一部分注入它的孩子。
的孩子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)
过渡.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
oraction.willEnter
被调度 - 为动画的持续时间设置了超时 (
optionSlice.duration
)。超时完成后:- 调用用户提供的 transitionEnd 函数 (
optionSlice.transitionEnd
) - 默认
actions.transitionComplete
发送
- 调用用户提供的 transitionEnd 函数 (
- 调用用户提供的 transitionBegin 函数 (
本质上,optionSlice 只允许用户传入一些选项。optionSlice.transitionBegin
并且optionSlice.transitionEnd
只是在动画进行时执行的可选功能,如果适合用例的话。我目前没有为我的组件传递任何东西,但我希望尽快把它变成一个库,所以我只是覆盖我的基础。
为什么我还要跟踪过渡状态?
根据进入的元素,退出的动画会发生变化,反之亦然。
例如,在上图中,当蓝色进入时,红色向右移动,当蓝色退出时,红色向左移动。然而,当绿色进入时,红色向左移动,当绿色离开时,红色向右移动。为了控制这一点,我需要知道当前转换的状态。
问题:
包含两个元素,TransitionGroup
一个进入,一个退出(由 react-router 控制)。它传递一个调用transitionState
它的孩子的道具。HOC(的Transition
子级TransitionGroup
)在动画过程中调度某些 redux 动作。正在进入的Transition
组件按预期接收 props 更改,但正在退出的组件被冻结。它的道具不会改变。
退出的总是没有收到更新的道具。我尝试过切换包装组件(退出和进入),问题不是由于包装组件造成的。
图片
React DOM 中的转换
在这种情况下,现有组件 Transition(Connect(Home))) 没有收到更新的道具。
任何想法为什么会这样?提前感谢所有帮助。
更新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
andcomponentWillLeave
钩子没有被调用。我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 源代码后,我意识到我把钥匙放错了地方。现在一切都很好。非常感谢你的帮忙!!