I'm new to React JavaScript. Previously I'm a desktop app developer with WPF and I uses Frame
and Page
extensively for my desktop apps. I want to achieve almost the same thing with React JavaScript. I want to make a bottom tab bar with React JavaScript. Here is my current work:
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;
As you might know from the codes above, my homepage is just a simple page with automatic counter increment per second:
The problem is my HomePage
can't be persistent. If I tried to change between tabs, it works but the counter at the home page is always reset to 0, meaning my home page and the other pages are recreated just because it is navigated between tabs. That's very weird to me as I created the <HomePage/>
at the App.js
line 14. What I'm doing wrong? Feel free to ask me if you need more details.
Maybe what I'm trying to do is more like this library:
https://github.com/lishengzxc/react-no-unmount-hide
I'm not really sure what that library does but I tried it and it doesn't work (just throws some weird error)
It's expected because new HomePage
instance is created each time Home tab is navigated.
This is the case for lifting the state up . If counter state should persist, it should be moved to a component that isn't destroyed on tab navigation, ie 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} />
)
});
...
These props should be used instead of local HomePage
state:
this.counterInterval = setInterval(this.props.increment);
and
Home page counter: {this.props.counter}
An alternative is to use Redux or React context API to manage global application state.
You should use the react-router-dom components for Linking and Navigation purposes.
This Project is based on something you want to achieve. Check it out
After struggling for hours, i finally managed to do it based on my idea. Here is the code for those who curious:
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;
Note: NumbersPage.js is just basically a page with very long lorem ipsum text.
Thats it, all states preserved without lifting. The scrolling position on NumbersPage.js is not preserved because i played with css display property there and thats normal :)
Thanks for the helps!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.