Реагировать на неправильный класс CSSTransition, используемый при выходе

ОБНОВИТЬ:

Для тех, кто сталкивается с той же проблемой: я нашел похожую проблему, размещенную на git-странице ReactTransitionGroup с решением там: https://github.com/reactjs/react-transition-group/issues/182.

Честно говоря, 90% решений взорвалось прямо у меня над головой, и я совсем ничего не понимаю, это единственная часть, которая имела для меня смысл (взято из React TransitionGroup и React.cloneElement не отправлять обновленные реквизиты с которой связан вопрос git):

Передача реквизита оставляющим детям? Теперь вопрос в том, почему обновленные реквизиты не передаются уходящему элементу? Ну, как бы он получил реквизит? Реквизиты передаются от родительского компонента к дочернему компоненту. Если вы посмотрите на пример JSX выше, вы увидите, что выходящий элемент находится в отсоединенном состоянии. У него нет родителя, и он отображается только потому, что хранит его в своем состоянии.

Я должен продолжить исследование и понять, как родительский и дочерний компоненты взаимодействуют. Я все еще новичок в веб-разработке, но все постепенно начинает обретать смысл.

------------------

Начальный вопрос задан:

Когда вы пытаетесь внедрить состояние дочерним элементам вашего объекта через React.cloneElement, уходящий компонент не является одним из этих дочерних элементов.

У меня есть небольшое приложение, которое содержит навигационное меню и маршруты, вложенные между контейнером коммутатора. При каждом щелчке по элементу навигации я проверяю, выше или ниже индекс, чтобы соответственно установить состояние анимации (влево или вправо), а затем сохраняю новый индекс элемента навигации, по которому щелкнули.

Каждый раз, когда я переключаю маршруты, применяемая анимация имеет либо fade-left, либо fade-right. Теперь все это работает нормально, но единственная проблема в том, что когда класс переключается вправо и влево, примененная анимация EXIT использует предыдущий данный класс.

Я понимаю, почему это происходит, потому что используется класс выхода, который изначально установлен, и когда новый класс применяется только при втором изменении, новый выход вступает в силу. Документация по переходу очень ограничена, и я ничего не смог найти.

const { Component } = React;
const { TransitionGroup, CSSTransition } = ReactTransitionGroup;
const { HashRouter, Route, NavLink, Switch, withRouter } = ReactRouterDOM;

var icons = [
 {icon: 'far fa-address-book active', path: '/'},
 {icon: 'fab fa-linkedin', path: '/linked'},
 {icon: 'fas fa-gamepad', path: '/hobbies'}
];

const Nav = props => {
  return (
   <div className="nav">
    <ul>
      { 
        icons.map((icon, index) => {
          return (
            <li
              key={index}
              onClick={() => props.clicked(index)}
              className={props.active == index ? 'active' : false}>
                <NavLink to={icon.path} className="NavLink">
                  <i className={icon.icon} key={icon.icon}></i>
                </NavLink>
            </li>
          );
        })
      }
    </ul>
  </div>
);
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentViewIndex: 0,
      animationDirection: 'right'
    }

   this.setIndex = this.setIndex.bind(this);
 }

 setIndex(index){
   const animationDirection = index < this.state.currentViewIndex ? 'left' : 'right';
   this.setState({currentViewIndex: index, animationDirection});
 }

  render () {
    const { location } = this.props;

    return (
      <div className="container">
        <Nav active={this.state.currentViewIndex} clicked={() => this.setIndex()} />
        <TransitionGroup>
          <CSSTransition
            key={location.pathname}
            classNames={`fade-${this.state.animationDirection}`}
            timeout={1000}>
            <Switch location={location}>
              <Route exact path="/" render={() => <h1>Home</h2>} />
              <Route path="/linked" render={() => <h1>Linked</h2>} />
              <Route path="/hobbies" render={() => <h1>Hobbies</h2>} />
            </Switch>
          </CSSTransition>
        </TransitionGroup>
      </div>
    );
   }
 }

const AppWithRouter = withRouter(App);

ReactDOM.render(<HashRouter><AppWithRouter /></HashRouter>, document.querySelector('#root'));

Классы CSS, применяемые к переходам:

.fade-left-enter {
  position: unset;
  transform: translateX(-320px);
}

.fade-left-enter.fade-left-enter-active {
  position: unset;
  transform: translateX(0);
  transition: transform .5s ease-in;
}

.fade-left-exit {
  position: absolute;
  bottom: 0;
  transform: translateX(0);
}

.fade-left-exit.fade-left-exit-active {
  transform: translateX(320px);
  transition: transform .5s ease-in;
}

.fade-right-enter {
  position: unset;
  transform: translateX(320px);
} 

.fade-right-enter.fade-right-enter-active {
  position: unset;
  transform: translateX(0);
  transition: transform .5s ease-in;
}

.fade-right-exit {
  position: absolute;
  bottom: 0;
  transform: translateX(0);
}

.fade-right-exit.fade-right-exit-active {
  transform: translateX(-320px);
  transition: transform .5s ease-in;
}

1 ответ

Решение

Причиной вашей проблемы является то, что выходящий компонент уже отсоединен и поэтому не получает никаких обновлений. Вы можете найти очень хорошее объяснение вашей проблемы здесь.

Вы можете использовать опору childFactory из <TransitionGroup> чтобы решить это:

childFactory

Возможно, вам придется применить реактивные обновления к ребенку, когда он выходит. Обычно это делается с помощью cloneElement, однако в случае выходящего дочернего элемента элемент уже удален и недоступен для потребителя.

Если вам нужно обновить ребенка по мере его выхода, вы можете предоставить childFactory, чтобы обернуть каждого ребенка, даже тех, которые уходят.

Попробуйте следующие изменения кода в вашем методе рендеринга:

render () {
  const { location } = this.props;

  const classNames = `fade-${this.state.animationDirection}`; // <- change here

  return (
    <div className="container">
      <Nav active={this.state.currentViewIndex} clicked={() => this.setIndex()} />
      <TransitionGroup 
        childFactory={child => React.cloneElement(child, { classNames })} // <- change here
      >
        <CSSTransition
          key={location.pathname}
          classNames={classNames} // <- change here
          timeout={1000}
        >
            <Switch location={location}>
              <Route exact path="/" render={() => <h1>Home</h2>} />
              <Route path="/linked" render={() => <h1>Linked</h2>} />
              <Route path="/hobbies" render={() => <h1>Hobbies</h2>} />
            </Switch>
        </CSSTransition>
      </TransitionGroup>
    </div>
  );

}

Другие вопросы по тегам