React JS-路由器更改时丢失道具

时间:2018-10-09 20:22:24

标签: javascript reactjs

我有一个带有一个元素列表的滑轨-当我导航到另一个容器并向后导航时-道具几乎丢失了吗?我收到以下错误。

错误:

    TypeError: Cannot read property 'props' of null

    const actionLength = activeRail.props.children.length - 1;


    const later = () => {
----> callback.apply(context, callbackArgs)
      timeout = null
    }

我不确定是什么引起了这个问题。

这是我的代码:

import React, { Component } from 'react';
import classNames from 'classnames';
import { withRouter } from 'react-router-dom';
import key from 'keymaster';
import posed, { PoseGroup } from 'react-pose';

// import { fetchingPageData } from '../../actions';
import channelData from '../../data/channel-data';
import "./styles.scss";

// components
import MusicCard from '../../components/MusicCard/';
import Card from '../../components/JioCardAnimated/';
import TimeAndDate from '../../components/TimeAndDate';
import NotificationCentre from '../NotificationCentre';

// rails
import {accountsRail} from './rails/accounts';
import {searchRail} from './rails/search';
import {homeRail} from './rails/home';
import {onNowRail} from './rails/on-now';
import {moviesRail} from './rails/movies';
import {showsRail} from './rails/shows';
import {musicRail} from './rails/music';
import {appsRail} from './rails/apps';
import {gamesRail} from './rails/games';
import {myFilesRail} from './rails/my-files';
import {settingsRail} from './rails/settings';


function throttle(callback, wait, context = this) {
  let timeout = null
  let callbackArgs = null

  const later = () => {
    callback.apply(context, callbackArgs)
    timeout = null
  }

  return function() {
    if (!timeout) {
      callbackArgs = arguments
      timeout = setTimeout(later, wait)
    }
  }
}

const colors = [
  '#1030a4'
];

const Item = posed.div({
  initialPose: 'closed',
  hidden: {
    height: "155px",
    opacity: 0,
    delay: 0
  },
  closed: {
    height: "155px",
    opacity: 1,
    delay: 0
  },
  open: {
    height: "650px",
    opacity: 0.6
  }
});

const Rail = posed.div({
  open: {
    opacity: 1,
    staggerChildren: 50
  },
  closed: {
    opacity: 0,
    delay: 50
  }
});

const Buddy = posed.div({
  STATIC: {
    x: 0,
    y: 0,
    width: 15,
    background: ({color}) => `${color}`,
    scale:1,
    opacity:1,
    transition: {
      type: 'spring',
    }
  },

  DOWN: {
    x: 0,
    y: 10,
    width: 15,
    background: ({color}) => `${color}`,
    scale: 0.9,
    opacity: 1,
    transition: {
      type: 'spring'
    }
  },

  UP: {
    x: 0,
    y: -10,
    width:20,
    background: ({color}) => `${color}`,
    scale:0.9,
    opacity:1,
    transition: {
      type: 'spring'
    },
  },

  EXPANDED: {
    x: -155,
    y: 0,
    width: 15,
    background: ({color}) => `${color}`,
    scale: 0.3,
    opacity: 0,
    transition: {
      type: 'spring',
      decay: 5,
      x: { duration: 100 }
    }
  },

  scrolled: {
    y: ({ target }) => target,
    transition: { type: 'spring' }
  },
});

const cardSizes = {
  small: 212 + 30,
  large: 568 + 30,
  music: 303 + 30
};


class MainMenu extends Component {
  constructor(props) {
    super(props);
    this.className = this.keyScope = 'main-menu';
    this.rails=[];

    //Base Rails
    this.baseRails = [
      accountsRail,
      searchRail,
      homeRail,
      onNowRail,
      moviesRail,
      showsRail,
      musicRail,
      appsRail,
      gamesRail,
      myFilesRail,
      settingsRail,
    ];

    this.state = {
      active: false,
      indexX: 1,
      indexY: 1, // 0,
      menuItems: channelData,
      isExpanded: false,
      expanding: false,
      expandDelay: 800,
      buddy: 'STATIC',
      rails: [...this.baseRails],
      transform: cardSizes.small,
      notifications:[],
      pageActive: false,
      shouldAnimate: true,
    };

    this.expandTimeout = undefined;
    this.UIElements = [];
    this.notificationTimeout = undefined;
    this.longpress = 1300;
    this.delay = undefined;
  }

  UNSAFE_componentWillMount() {
    // this.getItems();
    // this.setUIElements();
  }

  componentDidMount() {
    if(this.expandTimeout !== undefined) {
      clearTimeout(this.expandTimeout);
    }

    this.expandTimeout = setTimeout(()=> {
      this.onExpand(true);
    }, this.state.expandDelay*3);

    key('up', this.keyScope, this.onMoveVertical.bind(this, 'UP'));
    key('right', this.keyScope, this.onMoveHorizontal.bind(this, 'RIGHT'));
    key('down', this.keyScope, this.onMoveVertical.bind(this, 'DOWN'));
    key('left', this.keyScope, this.onMoveHorizontal.bind(this, 'LEFT'));
    key('enter', this.keyScope, this.onEnter.bind(this));
    key('m', this.keyScope, this.onToggleMenu.bind(this));
    key('c', this.keyScope, this.triggerNotification.bind(this));
    key.setScope(this.keyScope);
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
      const index = this.state.rails[this.state.indexY].children.length + 3;
      this.setIndex(index, true);
  }

  setIndex(indexX, home) {
    const offset = 585;
    const transform = home
      ? ((((indexX - 2) * cardSizes.large) + cardSizes.small + cardSizes.music) - offset )
      : ((((indexX - 1) * cardSizes.large) + cardSizes.small) - offset )

    this.setState({
      indexX,
      transform,
    });
  }

  triggerNotification() {
    if(this.state.notifications.length > 0) {
      // We already have a notification, we don't need another.
      return;
    }

    const notifications = [
      {
        id:'incoming-call',
        icon: '',
        avatar: '/images/contacts/contact-prof.png',
        primary:'Incoming call',
        secondary: 'Aanya Singh',
        action: 'Press OK to answer',
        active: true
      }
    ];
    this.setState({
      notifications: notifications
    }, () => {

      this.notificationTimeout = setTimeout(()=> {

        this.notificationTimeout = undefined;
        this.setState({
          notifications:[]
        });
      }, 10 * 1000);

    })
  }

  onEnter() {
    const { history } = this.props;
    const { active } = this.state;
    const activeComponent = this[`rails-${this.className}-${this.state.indexY}`].props.children[this.state.indexX];

    if(this.state.notifications.length > 0) {
      history.push( {pathname: 'calling'} );
    }

    if (active && activeComponent.props.cardType === 'guide') {
      history.push( {pathname: 'guide'} );
    }

    if(active && activeComponent.props.cardType === 'music') {
      history.push( { pathname: "music-splash" });
    }
  }

  onExpand(force) {
    this.setState({
      isExpanded: (force) ? force : !this.state.isExpanded,
      expanding: true,
      buddy:'EXPANDED'
    }, (force)=> {
      setTimeout(()=> {
        this.setState({
          expanding: false,
          buddy:'FOCUS'
        });
      }, 250);
    });
  }

  handleKeyUp = (direction) => {
    key(direction.toLowerCase(), this.keyScope, this.onMoveHorizontal.bind(this, direction));
    document.removeEventListener('keyup', this.handleKeyUp.bind(this, direction));
    this.delay = undefined;
  }

  onMoveHorizontal = throttle((direction) => {
    const targetX = this.state.indexX + ((direction === 'RIGHT') ? 1 : -1);
    const activeRail = this[`rails-${this.className}-${this.state.indexY}`];
    const actionLength = activeRail.props.children.length - 1;
    let transform;
    const cardTarget = direction === 'LEFT' ? targetX : targetX - 1;
    const activeCard = activeRail.props.children[cardTarget];
    const { cardType } = activeCard.props;
    const baseLength = (actionLength + 1) / 3;

    const exit = () => {
      const { indexX } = this.state;

      if ((direction === 'RIGHT' && indexX === (baseLength * 2)) || (direction === 'LEFT' && indexX === baseLength)) {
        key.unbind(direction.toLowerCase(), this.keyScope, this.onMoveHorizontal.bind(this, direction));

        setTimeout(() => {
          this.handleKeyUp(direction);
        }, 1500);

        return false;
      }
    };

    this.delay = setTimeout(exit, this.longpress);

    if (direction === 'RIGHT' && targetX > (baseLength * 2)) {

      if (this.state.indexY === 1) {
        transform = this.state.transform - (((baseLength - 2) * cardSizes.large) + cardSizes.small + cardSizes.music);
      } else {
        transform = this.state.transform - ((baseLength * cardSizes.large) - (cardSizes.large - cardSizes.small));
      }

      this.setState({
        indexX: baseLength + 1,
        shouldAnimate: false,
        transform: transform,
      }, () => {
        setTimeout(() => {
          this.setState({
            shouldAnimate: true,
            transform: this.state.transform + cardSizes.small,
          });
        }, 0)
      });

      return;
    } else if (direction === 'LEFT' && targetX < 2) {
      const index = baseLength + 1;

      if (this.state.indexY === 1) {
        transform = ((index - 2) * (cardSizes.large) + cardSizes.small + cardSizes.music) - cardSizes.small - 90;
      } else {
        transform = ((index - 1) * (cardSizes.large) + cardSizes.small) - cardSizes.small - 90;
      }

      this.setState({
        indexX: index,
        shouldAnimate: false,
        transform: transform,
      }, () => {
        setTimeout(() => {
          this.setState({
            shouldAnimate: true,
            transform: this.state.transform - cardSizes.small,
          });
        }, 0)
      });

      return;
    }

    if (direction === 'RIGHT') {
      if (cardType === 'guide') {
        transform = this.state.transform + cardSizes.small;
      } else if(cardType === 'music') {
        transform = this.state.transform + cardSizes.music;
      } else {
        transform = this.state.transform + cardSizes.large;
      }
    } else {
      if (cardType === 'guide') {
        transform = this.state.transform - cardSizes.small;
      } else if(cardType === 'music') {
        transform = this.state.transform - cardSizes.music;
      } else {
        transform = this.state.transform - cardSizes.large;
      }
    }

    if(this.state.indexY === 0 || this.state.indexY === 10 || this.state.indexY === 7 || targetX < 0 || targetX > actionLength) {
      return;
    } else {
      this.setState({
        indexX: targetX,
        transform,
      });
    }

  }, 100)


  onMoveVertical(direction) {
    const offset = (direction === 'DOWN') ? 1 : -1;
    const targetY = this.state.indexY + offset;

    if(targetY > this.state.rails.length-1 || targetY < 0) {
      return;
    }

    else {
      const isHome = targetY === 1;
      const index = this.state.rails[targetY].children.length + (isHome ? 3 : 2);
      this.setIndex(index, isHome);

      this.setState({
        indexY: targetY,
        isExpanded: false,
        buddy: direction,
      }, ()=>{

        if(this.expandTimeout !== undefined) {
          clearTimeout(this.expandTimeout);
        }

        setTimeout(()=> {
          this.setState({
            buddy:'STATIC'
          });
        }, this.state.expandDelay / 2 );

        this.expandTimeout = setTimeout(()=> {
          this.onExpand(true);
        }, this.state.expandDelay);
      });
    }
  }

  onToggleMenu() {
    if(this.notificationTimeout !== undefined) {
      clearTimeout(this.notificationTimeout);
      this.notificationTimeout = undefined
    }

    this.setState({
      active: !this.state.active,
      pageActive: !this.state.pageActive,
      notifications: []
    }, () => {
      if(!this.state.active) {
        setTimeout(() => {
          // LVNOTE: Loop and reset all
          for (const name in this.refs) {
            const component = this.refs[name];

            if(component.onResetIndex) {
              component.onResetIndex();
            }
          }
        }, 250);
      }
    });
  }

  renderRails() {
    const styleX = {
      transform: `translate(-${this.state.transform}px, 0px)`,
      transition: this.state.shouldAnimate ? 'transform 200ms' : '0ms',
    };

    const rails = this.state.rails.map((d,i) => {
      let railTitle = d.title;
      let railTheme = d.theme;
      let railBecause = d.because;
      let railIcon = d.icon;
      const active = this.state.indexY === i;

      const cards = d.children.map((item, j) => {
        const sup = (j === 0)
          ? `${railBecause}`
          : j === 2
            ? 'New shows added'
            : '';

        return (
          <Card
            id={`Square-${i}-${j}`}
            title={item.title}
            metadata={item.metadata}
            image={`/images/artwork/${item.image}`}
            key={`Square-${i}-${j}`}
            type={'SquareCard'}
            sup={sup}
            pose={(active && this.state.indexX === (j + (i === 1 ? 2 : 1))) ? 'IF' : 'OOF'}
          />
        );
      });

      //Push music card to first rail
      //*** PLEASE NOTE - Issue with this card not becoming selected on home rail
      if(i === 1) {
        cards.unshift(
          <MusicCard
            railType="Home"
            cardType="music"
            id={i}
            image='/images/music/Manto_SnehaKhanwalkar_1x1_01.png'
            metadata="Playlist"
            title="Manto"
            type={'MusicCard'}
          />
        );
      }

      cards.unshift(
        <Card
          cardType="guide"
          icon={railIcon}
          id={`Guide-1`}
          theme={railTheme}
          title={railTitle}
          type={'SquareCard'}
        />
      );

      const children = [...cards, ...cards, ...cards];
      const slides = [];

      React.Children.forEach(children, (child, index) => {
        const { cardType } = child.props;
        const initialPose = cardType === 'music' ? 'OOF' : 'OOF';

        slides.push(React.cloneElement(child, {
          key: `card-${index}`,
          pose: (active && this.state.indexX === index) ? 'IF' : 'OOF',
          initialPose,
          className: (active && this.state.indexX === index) ? 'active' : '',
        }));
      });

      return (
        <Item
          pose={(active && this.state.isExpanded) ? 'open' : (this.state.isExpanded) ? 'hidden' : 'closed'}
          active={active}
          isExpanded={this.state.isExpanded}
          currentIndex={this.state.indexY}
          className={classNames('menuItem', { active })}
          key={`pose-${i}`}
        >
          <div className="item-heading">
            <p>{railTitle}</p>
          </div>

          {active &&
            <Rail
              className='rail'
              pose={(active && this.state.isExpanded) ? 'open' : 'closed'}
              ref={c => this[`rails-${this.className}-${i}`] = c}
              style={styleX}
            >
              {slides}
            </Rail>
          }
        </Item>
      );
    });


    return rails;
  }

  render() {
    const { menuItems } = this.state;
    const yPos = 155 - (155 * this.state.indexY);
    const styleY = { transform: `translate(0px, ${yPos}px)` };
    const target = (this.state.isExpanded && this.state.expanding) ? 155 : 0;

    return (
      <div>
        <div className={classNames(this.className, { active: true, 'page-active': this.state.pageActive, })}>
          <TimeAndDate />
          <div className='marker'></div>

          <div className='buddy-holder'>
            <Buddy
              className='Buddy'
              pose={this.state.buddy}
              poseKey={target}
              color={colors[0]}
              target={target}
            />
          </div>

          <div className='menu' style={styleY}>

            <PoseGroup selectedItemId={this.state.indexY} lastIndex={menuItems.length}>
              {this.renderRails()}
            </PoseGroup>
          </div>
        </div>

        <NotificationCentre notifications={this.state.notifications} />
      </div>
    );
  }
}

export default withRouter(MainMenu);

1 个答案:

答案 0 :(得分:0)

您正在根据状态获取actionRail

<input id="fileUpload" name="fileUpload" type="file" accepts="image/*" class="fileFun file_uploader">

在更换路由器时,组件将丢失其状态,并且将无法找到要获取的组件。您可能要完全放弃使用组件内部的状态。