React TransitionGroup和React.cloneElement不会发送更新的道具(React TransitionGroup and React.cloneElement do not send updated props)
我跟随Chang Wang的教程,将
ReactTransitionGroup
和ReactTransitionGroup
(ReactTransitionGroup
( 第1 部分第2部分 )与Huan Ji的页面转换教程( Link )一起制作可重复使用的React转换。我面临的问题是
React.cloneElement
似乎没有将更新的道具传递给其中的一个孩子,而其他孩子确实会收到更新的道具。首先,一些代码:
TransitionContainer.js
TransitionContainer
是一个容器组件,与Huan Ji的教程中的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)
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
被调度实质上,optionSlice只允许用户传递一些选项。
optionSlice.transitionBegin
和optionSlice.transitionEnd
只是可选的函数,在动画发生时执行,如果适合用例。 我目前没有为我的组件传递任何东西,但我希望很快将它作为一个库,所以我只是覆盖我的基地。为什么我会跟踪过渡状态?
根据输入的元素,正在退出的动画会更改,反之亦然。
例如,在上图中,当蓝色进入时,红色向右移动,当蓝色退出时,红色向左移动。 但是,当绿色进入时,红色向左移动,当绿色退出时,红色向右移动。 为了控制这个,我需要知道当前转换的状态。
问题:
TransitionGroup
包含两个元素,一个输入,一个退出(由反应路由器控制)。 它传递一个名为transitionState
的道具给它的孩子。Transition
HOC(Transition
子女)通过动画过程发送某些redux动作。 正在输入的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)
我已经将我的
TransitionContainer
更新到了上面。 现在,componentWillEnter
和componentWillLeave
挂钩未被调用。 我在childFactory
函数中记录了React.cloneElement(child, {...})
,并且钩子(以及我定义的函数,如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来源的进一步检查,我意识到我把密钥放在了错误的地方。 一切都很好。 非常感谢你的帮助!!
I am following Chang Wang's tutorial for making reusable React transitions with HOCs and
ReactTransitionGroup
(Part 1 Part 2) in conjunction with Huan Ji's tutorial on page transitions (Link).The problem I am facing is that
React.cloneElement
does not seem to be passing updated props into one of its children, while other children do properly receive updated props.First, some code:
TransitionContainer.js
TransitionContainer
is a container component that is akin toApp
in Huan Ji's tutorial. It injects a slice of the state to it's children.The children of the
TransitionGroup
are all an instance of an HOC calledTransition
(code further down)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
is akin to Chang Wang's HOC. It takes some options, defines thecomponentWillEnter
+componentWillLeave
hooks, and wraps a component.TransitionContainer
(above) injectsprops.transitionState
into this HOC. However, sometimes the props do not update even if state changes (see The Problem below)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}/> } } }
options
Options have the following structure:
{ 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.} } }
Transition Lifecycle (onEnter or onLeave)
- When the component is mounted,
actions.registerComponent
is dispatched
componentWillMount
- When the component's
componentWillLeave
orcomponentWillEnter
hook is called, the corresponding slice of the options is sent todoTransition
- In doTransition:
- The user supplied transitionBegin function is called (
optionSlice.transitionBegin
)- The default
action.willLeave
oraction.willEnter
is dispatched- A timeout is set for the duration of the animation (
optionSlice.duration
). When the timeout is complete:
- The user supplied transitionEnd function is called (
optionSlice.transitionEnd
)- The default
actions.transitionComplete
is dispatchedEssentially, optionSlice just allows the user to pass in some options.
optionSlice.transitionBegin
andoptionSlice.transitionEnd
are just optional functions that are executed while the animation is going, if that suits a use case. I'm not passing anything in currently for my components, but I hope to make this a library soon, so I'm just covering my bases.Why Am I tracking transition states anyway?
Depending on the element that is entering, the exiting animation changes, and vice versa.
For example, in the image above, when the blue enters, red moves right, and when the blue exits, red moves left. However when the green enters, red moves left and when the green exits, red moves right. To control this is why I need to know the state of current transitions.
The Problem:
The
TransitionGroup
contains two elements, one entering, one exiting (controlled by react-router). It passes a prop calledtransitionState
to its children. TheTransition
HOC (children ofTransitionGroup
) dispatches certain redux actions through the course of an animation. TheTransition
component that is entering receives the props change as expected, but the component that is exiting is frozen. It's props do not change.It is always the one that is exiting that does not receive updated props. I have tried switching the wrapped components (exiting and entering), and the issues is not due to the wrapped components.
Images
Transition in React DOM
The exiting component Transition(Connect(Home))), in this case, is not receiving updated props.
Any ideas why this is the case? Thanks in advance for all the help.
Update 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)
I've updated my
TransitionContainer
to the above. Now, thecomponentWillEnter
andcomponentWillLeave
hooks are not being called. I logged theReact.cloneElement(child, {...})
in thechildFactory
function, and the hooks (as well as my defined functions likedoTransition
) are present in theprototype
attribute. Onlyconstructor
,componentWillMount
andcomponentWillUnmount
are called. I suspect this is because thekey
prop is not being injected throughReact.cloneElement
.transitionState
anddispatch
are being injected though.Update 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)
After further inspection of the TransitionGroup source, I realized that I put the key in the wrong place. All is well now. Thanks so much for the help!!
原文:https://stackoverflow.com/questions/41404232
最满意答案
我不确定这是否是最好的方法,但可以在“int_search”中使用“occurrence()”进行测试,即
solve ::int_search([occurs(when[t]) | t in TASK], input_order, indomain_random, complete) maximize obj;
另外,我建议您使用input_order / indomain_random之外的其他标签进行测试,例如first_fail / indomain_split等。
/哈坎
I'm not sure if this is the best approach, but can test using "occurs()" inside "int_search", i.e.
solve ::int_search([occurs(when[t]) | t in TASK], input_order, indomain_random, complete) maximize obj;
Also, I recommend that you test with some other labelings than input_order/indomain_random, e.g. first_fail/indomain_split etc.
/Hakan
相关问答
更多-
我不确定这是否是最好的方法,但可以在“int_search”中使用“occurrence()”进行测试,即 solve ::int_search([occurs(when[t]) | t in TASK], input_order, indomain_random, complete) maximize obj; 另外,我建议您使用input_order / indomain_random之外的其他标签进行测试,例如first_fail / indomain_split等。 /哈坎 I'm not su ...
-
它们意味着不同的东西 如果使用var ,变量将在您所在的范围内声明(例如函数)。 如果不使用var ,变量将通过范围层次上升,直到它遇到一个给定名称或全局对象(窗口,如果您在浏览器中执行)的变量,然后它将附加到该变量。 然后它非常类似于全局变量。 但是,它仍然可以删除delete (很可能是别人的代码谁也没有使用var )。 如果在全局范围内使用var ,变量是真正的全局变量,不能被删除。 在我看来,这是javascript中最危险的问题之一,应该被弃用,或至少提醒警告。 原因是,很容易忘记var并且偶然发 ...
-
从fixed_dictionaries策略中获取所需的条目,并从可选的词典策略中获取。 然后通过合并它们将它们组合成一个字典。 您可以在测试中执行此操作,也可以创建一个为您执行此操作的复合策略 。 Draw from a fixed_dictionaries strategy for the required entries and from a dictionaries strategy for the optional ones. Then combine them into one dictionar ...
-
Python中的可选变量(Optional variables in Python)[2022-06-30]
可选变量是有限形式的代数类型,主要用于静态类型语言。 在像Python这样的动态类型语言中,并不需要它们。 正如arshajii所说,你可以使用None (或者,如果你愿意,也可以使用你想要的任何其他哨兵)代表缺席价值。 然后,您只需检查变量是否具有预期的类型。 Optional variables, a limited form of algebraic typing, are primarily useful in statically typed languages. In dynamically t ... -
供将来参考:现在可以使用新的Android M运行时权限 。 仍然必须在清单中定义权限,因此,为了避免对具有较旧Android版本的用户强制执行权限,请使用新的
元素 For future reference: It is now possible with the new Android M runtime permissions. The permissions still have to be defined in manifest, so, to av ... -
我也在自己的CMS上工作,目前使用它: RewriteEngine on RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?url=$1 [L,QSA] 这会将domain.com/之后的整个网址推送到$_GET变量url 我有一个index.php页面,它处理所有url请求并相应地调用我的类或函数; function u ...
-
IMO您在Task类中暴露了太多信息。 我会这样做: public class Task { // what is title being used for? public string Title { get; set; } private IPlanningStrategy planningStrategy; public Task(IPlanningStrategy initialPlanningStrategy) { // Initiali ...
-
PHP - 使用接口,策略模式和可选方法参数(PHP - Using Interfaces, Strategy Pattern and Optional Method Parameters)[2024-02-05]
您始终在BackupStrategy->testConn($request, $port)方法中设置端口。 因此,如果某人没有给出一个端口,它将传递一个空字符串。 我会实现一个回退功能。 @simon说的是,你在控制器中使用了错误的方法名称。 You are always setting a port in BackupStrategy->testConn($request, $port) method. So if somebody doesn't give a port, it will pass an ... -
电子邮件字段在passportjs facebook策略中是可选的(email field is optional in passportjs facebook strategy)[2024-04-24]
我通过重新请求权限解决了这个问题。 事实证明我可以在passport.authenticate('facebook', {scope: ['email'], authType: 'rerequest'}) 。 我所做的是检查结果中是否存在emails字段,如果没有,我调用done时出错。 function(accessToken, refreshToken, profile, done) { if (profile.emails === undefined) { done('emai ... -
您可以使用getter延迟加载变量。 - (MyClass *) something { if(!_something) { _something = [MyClass new]; } return _something; } 因此,每次使用instance.something ,它都会对您进行检查并加载对象(如果它尚未存在)。 如果它是一个简单的单行并且你根本不想使用if ,你可以跳过关键字(我听说这更快,但现在无法验证): - (MyClass *) somet ...