Jay*_* S. 11 animation reactjs react-router reactcsstransitiongroup redux
我正在关注Chang Wang的教程,使用HOC和ReactTransitionGroup(第1 部分第2部分)与Huan Ji的页面转换教程(Link)一起制作可重复使用的React过渡.
我面临的问题是,React.cloneElement似乎没有将更新的道具传递给其中一个孩子,而其他孩子正确地接收更新的道具.
TransitionContainer.js
TransitionContainer是一个容器组件,类似于AppHuan 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)
Run Code Online (Sandbox Code Playgroud)
Transition.js
Transition类似于王昌的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}/>
}
}
}
Run Code Online (Sandbox Code Playgroud)
选项
选项具有以下结构:
{
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.}
}
}
Run Code Online (Sandbox Code Playgroud)
过渡生命周期(onEnter或onLeave)
actions.registerComponent将调度
componentWillMountcomponentWillLeave或componentWillEnter钩子时,会将相应的选项片发送给doTransitionoptionSlice.transitionBegin)action.willLeave或action.willEnter已分派optionSlice.duration)的持续时间设置超时.超时完成后:
optionSlice.transitionEnd)actions.transitionComplete是分派的从本质上讲,optionSlice只允许用户传入一些选项.optionSlice.transitionBegin并且optionSlice.transitionEnd只是在动画进行时执行的可选函数,如果它适合用例.我目前没有为我的组件传递任何东西,但我希望尽快将它变成一个库,所以我只是覆盖了我的基础.
为什么我会跟踪过渡状态呢?
根据输入的元素,退出动画会更改,反之亦然.
例如,在上图中,当蓝色进入时,红色向右移动,当蓝色退出时,红色向左移动.然而,当绿色进入时,红色向左移动,当绿色退出时,红色向右移动.为了控制这一点,我需要知道当前转换的状态.
它TransitionGroup包含两个元素,一个进入,一个退出(由react-router控制).它传递了一个叫它transitionState的孩子的道具.该TransitionHOC(儿童TransitionGroup)调度通过动画的过程中一定终极版动作.Transition正在进入的组件接收道具按预期更改,但是正在退出的组件被冻结.它的道具不会改变.
永远是那个没有收到更新道具的人.我已经尝试切换包装的组件(退出和进入),问题不是由于包装的组件.
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)
Run Code Online (Sandbox Code Playgroud)
我把我的内容更新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)
Run Code Online (Sandbox Code Playgroud)
在进一步检查TransitionGroup源代码之后,我意识到我把密钥放在了错误的位置.一切都很好.非常感谢你的帮助!!
Pau*_*l S 15
想象一下渲染下面的示例JSX:
<TransitionGroup>
<div key="one">Foo</div>
<div key="two">Bar</div>
</TransitionGroup>
Run Code Online (Sandbox Code Playgroud)
的<TransitionGroup>的children道具将会由要素:
[
{ type: 'div', props: { key: 'one', children: 'Foo' }},
{ type: 'div', props: { key: 'two', children: 'Bar' }}
]
Run Code Online (Sandbox Code Playgroud)
以上元素将存储为state.children.然后,我们更新<TransitionGroup>到:
<TransitionGroup>
<div key="two">Bar</div>
<div key="three">Baz</div>
</TransitionGroup>
Run Code Online (Sandbox Code Playgroud)
当componentWillReceiveProps被叫时,nextProps.children它将是:
[
{ type: 'div', props: { key: 'two', children: 'Bar' }},
{ type: 'div', props: { key: 'three', children: 'Baz' }}
]
Run Code Online (Sandbox Code Playgroud)
比较state.children和nextProps.children,我们可以确定:
1.
{ type: 'div', props: { key: 'one', children: 'Foo' }}正在离开
2.
{ type: 'div', props: { key: 'three', children: 'Baz' }}正在进入.
在常规的React应用程序中,这意味着<div>Foo</div>将不再呈现,但对于a的子节点则不是这样<TransitionGroup>.
<TransitionGroup>工作那么究竟<TransitionGroup>能够继续渲染不再存在的组件props.children呢?
<TransitionGroup>它children在状态中维护一个数组是什么呢?每当<TransitionGroup>接收到新的道具时,通过合并当前state.children和nextProps.children.来更新此数组.(初始数组是在constructor使用初始childrenprop时创建的).
现在,当<TransitionGroup>渲染时,它渲染state.children数组中的每个子节点.呈现后,它会调用performEnter以及performLeave任何进入或离开的孩子.这反过来将执行组件的转换方法.
在离开组件的componentWillLeave方法(如果有的话)已经完成执行之后,它将从state.children数组中删除它自己以便它不再呈现(假设它在离开时没有重新进入).
现在的问题是,为什么没有更新道具传递给离开元素?那么,它会如何获得道具?道具从父组件传递到子组件.如果查看上面的示例JSX,您可以看到leave元素处于分离状态.它没有父级,它只是被渲染,因为<TransitionGroup>它将它存储在其中state.
当你试图将状态注入到你的孩子<TransitionGroup>通过React.cloneElement时,将成分是不是这些孩子中的一个.
你可以把childFactory道具传给你的<TransitionGroup>.默认childFactory只返回子项,但您可以查看<CSSTransitionGroup>更高级的子工厂.
你可以通过这个子包装器将正确的道具注入到孩子(甚至是离开的孩子)中.
function childFactory(child) {
return React.cloneElement(child, {
transitionState,
dispatch
})
}
Run Code Online (Sandbox Code Playgroud)
用法:
var ConnectedTransitionGroup = connect(
store => ({
transitionState: state.transitions
}),
dispatch => ({ dispatch })
)(TransitionGroup)
render() {
return (
<ConnectedTransitionGroup childFactory={childFactory}>
{children}
</ConnectedTransitionGroup>
)
}
Run Code Online (Sandbox Code Playgroud)
React Transition Group最近在主要的React仓库中分离出来,你可以在这里查看它的源代码.通读非常简单.
| 归档时间: |
|
| 查看次数: |
3838 次 |
| 最近记录: |