React JavaScript防止组件卸载

时间:2018-12-18 07:01:15

标签: javascript reactjs tabs jsx

我是React JavaScript的新手。以前,我是使用WPF的桌面应用程序开发人员,并且在我的桌面应用程序中广泛使用FramePage。我想用React JavaScript达到几乎相同的目的。我想用React JavaScript做一个底部的标签栏。这是我目前的工作:

App.js

import './App.css';
import React, { Component } from 'react';
import BottomTabBar from './BottomTabBar/BottomTabBar';
import HomePage from './HomePage/HomePage';

class App extends Component {
  render() {
    let menuItems = [];

    menuItems.push({
      label: 'Home',
      faIcon: 'fas fa-home',
      content: (
        <HomePage/>
      )
    });

    menuItems.push({
      label: 'Numbers',
      faIcon: 'fas fa-ellipsis-h',
      content: (
        <h1>
          This is numbers page.
        </h1>
      )
    });

    menuItems.push({
      label: 'Notifications',
      faIcon: 'fas fa-bell',
      content: (
        <h1>
          This is notifications page.
        </h1>
      )
    });

    menuItems.push({
      label: 'Menu',
      faIcon: 'fas fa-bars',
      content: (
        <h1>
          This is menu page.
        </h1>
      )
    });

    return (
      <div 
        className='App'
      >
        <BottomTabBar
          menuItems={menuItems}
        />
      </div>
    );
  }
}

export default App;

BottomTabBar.js

import './BottomTabBar.css';
import '../Ripple.css';
import React, { Component } from 'react';

class BottomTabBar extends Component {
  constructor() {
    super();

    this.state = {
      content: null,
      selectedTabIndex: 0,
    };
  }

  handleClick = (index) => {
    // Changing content.
    this.setState({
      selectedTabIndex: index
    });
  }

  render() {
    // Putting them all.
    return (
      <div
        className='BottomTabBar'
      >
        <div
          className='Content'
        >
          {this.props.menuItems[this.state.selectedTabIndex].content}
        </div>

        <div
          className='IconsBar'
        >
          {
            this.props.menuItems.map((menuItem, i) => {
              return (
                <div
                  className="MenuItem Ripple"
                  key={i}
                  onClick={()=>this.handleClick(i)}
                >
                  <div
                    className="Gap"
                  />
                  <div
                    className="Icon"
                  >
                    <div
                      className={menuItem.faIcon}
                    />
                  </div>
                  <div
                    className="Gap"
                  />
                  <div
                    className="Text"
                  >
                    {menuItem.label}
                  </div>
                </div>
              )
            })
          }
        </div>
      </div>
    );
  }
}

export default BottomTabBar;

HomePage.js

import './HomePage.css';
import React, { Component } from 'react';

class HomePage extends Component {
  constructor() {
    super();

    this.state = {
      counter: 0,
    };
  }

  componentDidMount() {
    this.counterInterval = setInterval(() => {
      this.setState((prevState) => ({
        counter: prevState.counter + 1
      }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.counterInterval);
  }

  render() {
    return (
      <div
        className='HomePage'
      >
        Home page counter: {this.state.counter}
      </div>
    );
  }
}

export default HomePage;

您可能从上面的代码中知道,我的主页只是一个简单的页面,具有每秒自动递增的计数器:

普通首页:
Normal home page

问题是我的HomePage不能持久。如果我尝试在选项卡之间进行切换,那么它可以工作,但是主页上的计数器始终重置为0,这意味着仅在选项卡之间进行导航时,才会重新创建我的主页和其他页面。当我在<HomePage/>第14行创建App.js时,这对我来说很奇怪。我做错了什么?随时问我是否需要更多详细信息。

也许我想做的更像是这个库:

https://github.com/lishengzxc/react-no-unmount-hide

我不太确定该库的功能,但是我尝试了一下,但是它不起作用(只是抛出一些奇怪的错误)

3 个答案:

答案 0 :(得分:1)

这是可以预期的,因为每次导航 Home 标签时都会创建新的HomePage实例。

lifting the state up就是这种情况。如果计数器状态应保持不变,则应将其移至选项卡导航中不会破坏的组件,即App

class App extends Component {
  state = { counter: 0 };

  incrementCounter = () => setState((state) => ({ counter: state.counter + 1 }));

  render() {
    let menuItems = [];

    menuItems.push({
      label: 'Home',
      faIcon: 'fas fa-home',
      content: (
        <HomePage counter={this.state.counter} increment={this.incrementCounter} />
      )
    });
    ...

应使用这些道具代替本地HomePage状态:

this.counterInterval = setInterval(this.props.increment);

Home page counter: {this.props.counter}

一种替代方法是使用Redux或React上下文API来管理全局应用程序状态。

答案 1 :(得分:0)

您应将react-router-dom组件用于链接和导航。

该项目基于您想要实现的目标。签出

https://github.com/onejeet/image-sharing-app

答案 2 :(得分:0)

经过几个小时的努力,我终于根据自己的想法做到了。这是那些好奇的代码:

App.js:

import './App.css';
import React, { Component } from 'react';
import BottomTabBar from './BottomTabBar/BottomTabBar';
import HomePage from './HomePage/HomePage';
import NumbersPage from './NumbersPage/NumbersPage';

class App extends Component {
  render() {
    let menuItems = [];

    menuItems.push({
      label: 'Home',
      faIcon: 'fas fa-home'
    });

    menuItems.push({
      label: 'Numbers',
      faIcon: 'fas fa-ellipsis-h'
    });

    return (
      <div 
        className='App'
      >
        <BottomTabBar
          menuItems={menuItems}
        >
          <HomePage/>
          <NumbersPage/>
        </BottomTabBar>
      </div>
    );
  }
}

export default App;

BottomTabBar.js:

import './BottomTabBar.css';
import '../Ripple.css';
import React, { Component } from 'react';

class BottomTabBar extends Component {
  constructor() {
    super();

    this.state = {
      selectedIndex: 0,
    };

    console.log('Initialized bottom tab bar');
  }

  handleClick = (index) => {
    // Changing content.
    this.setState({
      selectedIndex: index
    });
  }

  render() {
    return (
      <div
        className='BottomTabBar'
      >
        <div
          className='Content'
        >
          {
            this.props.children.map((child, i) => {
              return (
                <div
                  style={{
                    display: i == this.state.selectedIndex
                    ? 'block'
                    : 'none'
                  }}
                  key={i}
                >
                  {child}
                </div>
              )
            })
          }
        </div>

        <div
          className='IconsBar'
        >
          {
            this.props.menuItems.map((menuItem, i) => {
              return (
                <div
                  className="MenuItem Ripple"
                  key={i}
                  onClick={()=>this.handleClick(i)}
                >
                  <div
                    className="Gap"
                  />
                  <div
                    className="Icon"
                  >
                    <div
                      className={menuItem.faIcon}
                    />
                  </div>
                  <div
                    className="Gap"
                  />
                  <div
                    className="Text"
                  >
                    {menuItem.label}
                  </div>
                </div>
              )
            })
          }
        </div>
      </div>
    );
  }
}

export default BottomTabBar;

HomePage.js:

import './HomePage.css';
import React, { Component } from 'react';

class HomePage extends Component {
  constructor() {
    super();

    this.state = {
      counter: 0,
    };

    console.log('Initialized home page');
  }

  componentDidMount() {
    this.counterInterval = setInterval(() => {
      this.setState((prevState) => ({
        counter: prevState.counter + 1
      }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.counterInterval);
  }

  render() {
    return (
      <div
        className='HomePage'
        style={this.props.style}
      >
        Home page counter: {this.state.counter}
      </div>
    );
  }
}

export default HomePage;

注意:NumbersPage.js基本上只是一个带有很长的lorem ipsum文本的页面。

就是这样,所有状态都保留下来而没有解除。不会保留NumbersPage.js上的滚动位置,因为我在那儿使用了css display属性,而那是正常的:)

感谢您的帮助!