首页 \ 问答 \ React TransitionGroup和React.cloneElement不会发送更新的道具(React TransitionGroup and React.cloneElement do not send updated props)

React TransitionGroup和React.cloneElement不会发送更新的道具(React TransitionGroup and React.cloneElement do not send updated props)

我跟随Chang Wang的教程,将ReactTransitionGroupReactTransitionGroupReactTransitionGroup第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
  • 当组件的componentWillLeavecomponentWillEnter挂钩被调用时,相应的选项片段被发送到doTransition
  • 在doTransition中:
    • 用户提供的transitionBegin函数被调用( optionSlice.transitionBegin
    • 将分派默认的action.willLeaveaction.willEnter
    • 超时设置为动画的持续时间( optionSlice.duration )。 超时完成时:
      • 用户提供的transitionEnd函数被调用( optionSlice.transitionEnd
      • 默认的actions.transitionComplete被调度

实质上,optionSlice只允许用户传递一些选项。 optionSlice.transitionBeginoptionSlice.transitionEnd只是可选的函数,在动画发生时执行,如果适合用例。 我目前没有为我的组件传递任何东西,但我希望很快将它作为一个库,所以我只是覆盖我的基地。

为什么我会跟踪过渡状态?

在这里输入图像描述

根据输入的元素,正在退出的动画会更改,反之亦然。

例如,在上图中,当蓝色进入时,红色向右移动,当蓝色退出时,红色向左移动。 但是,当绿色进入时,红色向左移动,当绿色退出时,红色向右移动。 为了控制这个,我需要知道当前转换的状态。

问题:

TransitionGroup包含两个元素,一个输入,一个退出(由反应路由器控制)。 它传递一个名为transitionState的道具给它的孩子。 Transition HOC( Transition子女)通过动画过程发送某些redux动作。 正在输入的Transition组件接收到预期的道具更改,但正在退出的组件已冻结。 它的道具不会改变。

它总是退出的那个没有收到更新的道具。 我尝试切换封装的组件(退出和输入),并且问题不是由于封装的组件。

图片

屏幕过渡: 过渡

React DOM中的转换

Transition2

在这种情况下,退出组件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更新到了上面。 现在, componentWillEntercomponentWillLeave挂钩未被调用。 我在childFactory函数中记录了React.cloneElement(child, {...}) ,并且钩子(以及我定义的函数,如doTransition )出现在prototype属性中。 只调用constructor componentWillMountcomponentWillUnmount 。 我怀疑这是因为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来源的进一步检查,我意识到我把密钥放在了错误的地方。 一切都很好。 非常感谢你的帮助!!


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.cloneElementdoes 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 to App 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 called Transition (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 the componentWillEnter + componentWillLeave hooks, and wraps a component. TransitionContainer (above) injects props.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 or componentWillEnter hook is called, the corresponding slice of the options is sent to doTransition
  • In doTransition:
    • The user supplied transitionBegin function is called (optionSlice.transitionBegin)
    • The default action.willLeave or action.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 dispatched

Essentially, optionSlice just allows the user to pass in some options. optionSlice.transitionBegin and optionSlice.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?

enter image description here

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 called transitionState to its children. The Transition HOC (children of TransitionGroup) dispatches certain redux actions through the course of an animation. The Transition 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

On-Screen Transition: Transition

Transition in React DOM

Transition2

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, the componentWillEnter and componentWillLeave hooks are not being called. I logged the React.cloneElement(child, {...}) in the childFactory function, and the hooks (as well as my defined functions like doTransition) are present in the prototype attribute. Only constructor, componentWillMount and componentWillUnmount are called. I suspect this is because the key prop is not being injected through React.cloneElement. transitionState and dispatch 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
更新时间:2022-03-29 11:03

最满意答案

我不确定这是否是最好的方法,但可以在“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这样的动态类型语言中,并不需要它们。 正如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 ...
  • 您始终在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 ...
  • 我通过重新请求权限解决了这个问题。 事实证明我可以在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 ...

相关文章

更多

最新问答

更多
  • 您如何使用git diff文件,并将其应用于同一存储库的副本的本地分支?(How do you take a git diff file, and apply it to a local branch that is a copy of the same repository?)
  • 将长浮点值剪切为2个小数点并复制到字符数组(Cut Long Float Value to 2 decimal points and copy to Character Array)
  • OctoberCMS侧边栏不呈现(OctoberCMS Sidebar not rendering)
  • 页面加载后对象是否有资格进行垃圾回收?(Are objects eligible for garbage collection after the page loads?)
  • codeigniter中的语言不能按预期工作(language in codeigniter doesn' t work as expected)
  • 在计算机拍照在哪里进入
  • 使用cin.get()从c ++中的输入流中丢弃不需要的字符(Using cin.get() to discard unwanted characters from the input stream in c++)
  • No for循环将在for循环中运行。(No for loop will run inside for loop. Testing for primes)
  • 单页应用程序:页面重新加载(Single Page Application: page reload)
  • 在循环中选择具有相似模式的列名称(Selecting Column Name With Similar Pattern in a Loop)
  • System.StackOverflow错误(System.StackOverflow error)
  • KnockoutJS未在嵌套模板上应用beforeRemove和afterAdd(KnockoutJS not applying beforeRemove and afterAdd on nested templates)
  • 散列包括方法和/或嵌套属性(Hash include methods and/or nested attributes)
  • android - 如何避免使用Samsung RFS文件系统延迟/冻结?(android - how to avoid lag/freezes with Samsung RFS filesystem?)
  • TensorFlow:基于索引列表创建新张量(TensorFlow: Create a new tensor based on list of indices)
  • 企业安全培训的各项内容
  • 错误:RPC失败;(error: RPC failed; curl transfer closed with outstanding read data remaining)
  • C#类名中允许哪些字符?(What characters are allowed in C# class name?)
  • NumPy:将int64值存储在np.array中并使用dtype float64并将其转换回整数是否安全?(NumPy: Is it safe to store an int64 value in an np.array with dtype float64 and later convert it back to integer?)
  • 注销后如何隐藏导航portlet?(How to hide navigation portlet after logout?)
  • 将多个行和可变行移动到列(moving multiple and variable rows to columns)
  • 提交表单时忽略基础href,而不使用Javascript(ignore base href when submitting form, without using Javascript)
  • 对setOnInfoWindowClickListener的意图(Intent on setOnInfoWindowClickListener)
  • Angular $资源不会改变方法(Angular $resource doesn't change method)
  • 在Angular 5中不是一个函数(is not a function in Angular 5)
  • 如何配置Composite C1以将.m和桌面作为同一站点提供服务(How to configure Composite C1 to serve .m and desktop as the same site)
  • 不适用:悬停在悬停时:在元素之前[复制](Don't apply :hover when hovering on :before element [duplicate])
  • 常见的python rpc和cli接口(Common python rpc and cli interface)
  • Mysql DB单个字段匹配多个其他字段(Mysql DB single field matching to multiple other fields)
  • 产品页面上的Magento Up出售对齐问题(Magento Up sell alignment issue on the products page)