How can I re-render(refresh) bottom tab bar in react native? - reactjs

I am struggling with the
import { create Bottom Tab Navigator } from '#react-navigation/bottom-tabs';
I want re-render bottom tab after removing cart Items from cart component by dispatch action and my focused tab is cart tab,
and i am also used a custom components for getting cart Count from state below i am sharing the code of bottom tab icon.
export class Cart Badge Icon extends Component {
constructor(props){
super(props);
}
render() {
return (
<>
<Image source={this.props.is-focused ? IMP_CONT.CART_ACTIVE :
MG_CONT.CART_INACTIVE} style={{ width: scale(25), height: scale(22) }} />
{this.props.counterproductive ?
(<View
>
<Text>{this.props.cart Count}
</Text>
</View>
) : null}
</>
)
}
}
cont map State To Props = (state) => ({
cart Has Product Flag: state.cart.cart Has Product,
cart Count: state.cart.cart Count,
})
export default connect(map State To Props)(Cart Badge Icon)
and i have import this component in my bottom Tab like this <Cart Badge Icon/>

after one day's i fond the solution,
i dispatch a action for receiving cartCount and i already get cartCout in CartBadgeIcon component.

Related

Nativebase Tabs not changing content when changing page value

I am having problems with navigating to a tab from another screen. When I am on the page clicking the tabs, the content switches fine, but when I try to set the active tab from another component before navigating to that screen, I am having issues with going to the specified tab.
I've tried passing parameters through the navigation to the parent screen like this: this.props.navigation.navigate('Menu', {goToOrderDetails: true}) and then inside the Menu screen, I made a componentFocused function where every time the page is in focus, I am checking if that goToOrderDetails parameter is true, and if it is, I am manually setting the state variable that is being used to set the active tab. This, however, is not working and just returns directly to the default tab (0), however, the styling color indicators are on the correct tab, but the content is that of the first tab.
This is how I am currently trying to do it:
Here is the Menu screen:
import React, {Component} from 'react';
import {observer} from "mobx-react";
import {ScrollView} from 'react-native';
import {Container, Tab, Tabs, ScrollableTab} from 'native-base';
import OrderHistory from './OrderHistory';
import Favorites from '../components/Favorites';
import Heading from '../components/Heading';
import store from '../store/store';
import i18n from 'i18n-js';
class Menu extends Component {
constructor() {
super();
this.subs;
this.state = {
activeTab: 0,
initialPage: 0
};
}
getTab() {
if(this.props.navigation.state.params){
if(this.props.navigation.state.params.page == 'orderhistory') {
return 2;
}if(this.props.page == 'favorites'){
return 1;
}else{
return 0;
}
} else {
if (this.props.navigation.getParam('orderHistory') || store.toOrderHistory == true) {
return 2;
} else if (this.props.navigation.getParam('favorites')) {
return 1;
}
store.toOrderHistory.set(false);
}
}
componentDidMount() {
this._componentFocused();
this._sub = this.props.navigation.addListener(
'didFocus',
this._componentFocused
);
}
_componentFocused = () => {
store.updateActiveTab(this.getTab());
}
updateTab(tab) {
store.updateActiveTab(tab.i);
}
render() {
return (
<Container>
<Heading title={'Menu'} />
<Tabs onChangeTab={(tab) => {this.updateTab.bind(this, tab)}}
page={store.activeTab.get()}
renderTabBar={()=> <ScrollableTab />}>
<Tab heading={i18n.t('menu.all_tab')}>
<ScrollView>
{this.displayMenu()}
</ScrollView>
</Tab>
<Tab heading={i18n.t('menu.fav_tab')}>
<Favorites></Favorites>
</Tab>
<Tab heading={i18n.t('menu.recent_tab')}>
<OrderHistory></OrderHistory>
</Tab>
</Tabs>
</Container>
)
};
}
export default observer(Menu);
And I am trying to send the navigation parameter from the order details screen to this screen like this: this.props.navigation.navigate('Menu',{goToOrderDetails: true}) and when that parameter is true, I am updating the store activeTab value like this: store.updateActiveTab.set(2).
The strangest part is that if I console.log the tab value, it is 2! Which is the correct one I need it to be on! And as I mentioned earlier, the color of the title is also the active color! It is just the content and the color underline that are not on that tab.
Is it possible that it is necessary to refresh the entire tabs component so that I can change the active tab from another screen? I am not sure why this behavior is occurring and could use some help!
Here is a screenshot of what happens after I have manually changed the active tab from the order details component!
I am also using same tab and implement as below:
<Tabs ref={(c) => { this.bottomTabRef = c; }} initialPage={0} page={pageNum} onChangeTab={({ from, i }) => { this.setState({ pageNum: i }); this.bottomTabRef.goToPage(0); }}>
<Tab heading="tab1">
<Text> Tab1 </Text>
</Tab>
<Tab heading="tab2">
<Text> Tab2 </Text>
</Tab>
</Tabs>

How to trigger a function from another component's onclick event

So I'm new in ReactJS and there's a cool company offering a junior react developer position if I solve their test project correctly. In the project, I have to draw four svg components and handle some simple events with only one user interaction which is a button click event. So everything seems quite simple but the thing is, I'm stucked in the middle of the project.
I want to give a shake animation to one of the components with a click event in the button component. When Button is clicked, the Tree should shake for 3 seconds and one other action will be triggered after that. I prepared a css animation and I want to change the class of the Tree component every time when the Button component clicked. Could not find out how to do so.
So here is my button component
class ShakeButton extends React.Component {
this.props.onClick(this.props.activeClass==='shake')
render() {
return (
<g id="shakeButton" transform="scale(0.15,0.15), translate(3000,-1000)" onClick={this.handleClick}>
//a good amount of svg content here
</g>
)
}
}
export default ShakeButton
and here my Tree component (which is gonna change its class name for css animation)
import React, { Components } from "react";
import "./cssTree.css";
class AppleTree extends React.Component{
// state = {
// activeClass: ''
// } n
// handleClick = () => this.props.onClick(this.props.activeClass==='shake')
render() {
return (
<g className="" id="appleTree" >
<g transform="translate(-1000 -1300) scale(1.5)">
//again, some crowded svg content here
</g>
</g>
)
}
}
export default AppleTree
I collect all of my components in a component named <Canvas /> and render it inside the App.js
You could try lift the onClick method and state up, meaning get it in your parent component (App I suppose), then pass it down to child components.
class App extends React.Component{
constructor(props) {
super(props);
this.state({
className: ''
});
}
handleClick = () => {
this.setState({ className: 'shake' });
}
render (
return (
// all your components
<Button clickHandler={this.handleClick} />
<AppleTree class={this.state.className} />
)
)
}
// Button component: Stateless if it doesn't need it's own state
const Button = ({ clickHandler }) => {
return (
<g id="shakeButton" transform="scale(0.15,0.15), translate(3000,-1000)" onClick={() => clickHandler()}>
//a good amount of svg content here
</g>
)
}
// Stateless if it doesn't need its own state
const AppleTree = ({ class }) => {
return (
<g className={class} id="appleTree" >
<g transform="translate(-1000 -1300) scale(1.5)">
//again, some crowded svg content here
</g>
</g>
)
}

React Native : Regarding State and Callback handlers

So I am still learning React Native and I am trying to build a very simple app to understand state, events in React Native.
Here in this app I display a button titled "first" as soon as the app is rendered on the screen.
Upon clicking that button a modal is displayed. This modal contains a button titled "second".
The objective is to hide the modal upon "onPress" of the "second" button.
Thsi is my code.
import React from 'react';
import { StyleSheet, Text, View, Button, Modal } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.showModal = this.showModal.bind(this);
}
state = {
modalVisible: false,
}
hideModal = () => {
console.log("Btnpress pressed");
this.setState({modalVisbile: false});
}
showModal() {
console.log("BtnPress1 pressed");
this.setState({modalVisible: true});
}
render() {
return (
<View style={styles.container}>
<Button title="first"
onPress={this.showModal}
disabled={this.state.modalVisible} />
<Modal
animationType= "slide"
transparent= {false}
visible={this.state.modalVisible}
>
<Button
title="second"
onPress={this.hideModal}
disabled={!this.state.modalVisible}
/>
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
What Happens
a) There are no errors.
b) The app is rendered successfully and display the button "first".
c) When the "first" button is clicked, the second button("second") contained in the modal is rendered as expected.
d) But when the "second" button is clicked the "first" button is not rendered.
My understanding is that upon "onPress" event on "second" button the below callback is invoked which changes the state.
onPress={this.hideModal}
After changing that state (which would now be modalVisible = false) the button titled "first" will be rendered. But this is not happening.
Can some one tell em what I am doing wrong ?
In your code, you misspelled visible, if you correct the spelling, it looks like it will work
hideModal = () => {
console.log("Btnpress pressed");
this.setState({modalVisible: false}); /*you had modalVisbile*/
}

How to pass the navigator object to the components used in the react natives root file?

Below is my app component:
/**
* Class app from where the app bootstraps
*/
export default class App extends Component {
constructor(props) {
super(props);
}
// This is where all your routes will be processed
renderScene(route, navigator) {
// Set a variable to get the route
let RouteComponent = route.component;
_navigator = navigator;
// With props return the components
return (
<RouteComponent navigator={navigator} {...route.passProps} />
);
}
static navigationBarRouteMapper = openControlPanel => ({
LeftButton: function(route, navigator, index, navState) {
return (
// Hamburger icon which on clicked opens the menu
<TouchableOpacity style={navBarStyle.left} onPress={() => openControlPanel()}>
<View>
{hamburgerIcon}
</View>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
return (
// cart menu
<TouchableWithoutFeedback onPress={() => dismissKeyboard()}>
<View style={navBarStyle.right}>
{cartIcon}
<View style={navBarStyle.counter}>
<Text style={navBarStyle.counterText}>20</Text>
</View>
</View>
</TouchableWithoutFeedback>
);
},
Title: function(route, navigator, index, navState) {
// Title of the route
return (
<TouchableWithoutFeedback onPress={() => dismissKeyboard()}>
<View style={navBarStyle.titleWrap}>
<Text style={navBarStyle.title}>{route.title.toUpperCase()}</Text>
</View>
</TouchableWithoutFeedback>
);
}
})
// Close the menu drawer
closeControlPanel() {
this._drawer.close();
}
// open the menu drawer
openControlPanel() {
this._drawer.open();
dismissKeyboard();
}
// On clicking the menu item, this function routes to that menu item page
getNavigator(route) {
this.refs.navigator.replace(route);
this.closeControlPanel();
}
render() {
return (
<Drawer
ref={ (ref) => { this._drawer = ref; } }
type="overlay"
content={<Menu navigator={this.getNavigator.bind(this)} menuItems={menu} closeControlPanel={this.closeControlPanel.bind(this)} />}
onOpenStart={() => dismissKeyboard()}
tapToClose={true}
openDrawerOffset={0.2}
panCloseMask={0.2}
panOpenMask={20}
acceptPan={true}
closedDrawerOffset={-3}
styles={drawerStyle}
tweenHandler={(ratio) => ({
// This code will maintain the opacity for main
// Whilst the opacity for the mainOverlay on the screen will be faded.
main: { opacity: 1 },
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'black',
}
})}>
<Navigator
initialRoute={homeScene}
renderScene={this.renderScene}
ref="navigator"
// Navbar of the app
navigationBar={
<Navigator.NavigationBar
routeMapper={App.navigationBarRouteMapper(this.openControlPanel.bind(this))}
style={navBarStyle.navBar}
/>
}
/>
<EditSearch />
</Drawer>
);
}
}
At the very bottom you'll see the <EditSearch> component which contains two text inputs. This component would be common to all the pages in the app except the home page.
Hence, I wanna know on which page or scene am I currently on so that I could check whether the scene/page is the home page or not. If it's the home page I would hide the component else I would show it for all other pages.
I tried passing the navigator via ref like so:
<EditSearch nav={this.refs.navigator} />
But, I get undefined on the <EditSearch> component and the view doesn't re-render when the page changes as it doesn't detect any state change.
I can do something like:
this.state = {
currentRoute: 'home'
}
And then change this state when route changes. But, I cannot change state within renderScene as setting state in renderScene will lead into infinite loop. If I could set this state with the title of the page when route changes, I could then send that state to <EditSearch> component.
I am pretty confused how can I pass current route information to this common component. Thanks in anticipation.
Okay, I found a solution but I am unsure if it's appropriate.
So below is my solution.
First I created the state and function to set that state:
constructor(props) {
super(props);
this.state = {
isHome: false
};
this.setIsHome = this.setIsHome.bind(this);
}
setIsHome(flag) {
// Set state to let component know whether the page is home or not
this.setState({
isHome: flag ? flag : false
});
}
Then passed the function to all the pages where those pages can either hide or show <EditSearch> component:
<RouteComponent navigator={navigator} {...route.passProps} setIsHome={this.setIsHome} />
Then I passed the isHome state to the <EditSearch> component:
<EditSearch isHome={ this.state.isHome } />
And this is how I call the function in the home component:
componentWillMount() {
this.props.setIsHome(true);
}
componentWillUnmount() {
this.props.setIsHome(false);
}
This is how I show/hide the component. I hope it's helpful. But, would be really nice to know how to pass navigator object to such components.
Here's a solution from Drawer Github issues.
Found a quick workaround for this but it is not ideal. If you wrap the
Navigator with the Drawer component, then inside of the
Navigator.NavigationBar you can set this.navigator = this.navigator ||
navigator and then you can pass the navigator to your content
component for the Drawer.
<Drawer
content={<Main navigator={this.navigator}/>}
type='overlay'>
<Navigator
renderScene={renderScene}
navigationBar={
<Navigator.NavigationBar
routeMapper={{
LeftButton: (route, navigator, index, navState) => {
this.navigator = this.navigator || navigator;
return (
<View>
<Text>Something</Text>
</View>
);
}
}}
/>
/>
</Drawer>
https://github.com/root-two/react-native-drawer/issues/187#issuecomment-231461585

Differences passing Redux state through passProps with Navigator or binding state within Container

Testing React Native with Redux, and react-redux, I've found that the content of the state tree of Redux is not reflected in the display if the state is not passed through a Container.
To test this i use
react-native 0.19.0
react-redux 4.1.2
redux 3.1.7
as #alinzin explain in https://github.com/alinz/example-react-native-redux with all the tricks to setup this project. Are out there any other good solution?
The next example show a simple string as state in Redux store, and a simple action for enlarge the string.
Here is the reduce and the data as a single string in the state.
//Reducer
const reducer = (state = { data: '[][][]' }, action = {}) => {
switch (action.type) {
case ENLARGE:
return Object.assign({}, state, {
data: state.data+'[]'
})
default:
return state
}
}
The registred App is like this, nothing new. The initial route launches a Container for App1Component. This is the way for good binding state and Components (I belive)
export default class App extends Component {
render () {
return (
<Provider store={store}>
<Navigator style={{flex: 1}}
initialRoute={{
component: Container,
}}
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component navigator={navigator} route={route} {...route.passProps} />
</View>
);
}}
/>
</Provider>
)
}
}
The main content for both component App1Component and App2Componenet, is a simple component and just display the string, and show a 'button' to dispatch the enlarge action.
class MainContent extends Component{
render() {
return (
<View>
<Text>{this.props.data}-{this.props.data.length}</Text>
<TouchableOpacity onPress={()=>{this.props.enlarge()}} >
<Text>Click to Enlarge me!</Text>
</TouchableOpacity>
<Text> </Text>
</View>
)
}
}
There are no differences between components App1 and App2.
Just App1Component is invoked through a Container (and bind state and actions), and App2Component invoked (from App1Component) by pushing with Navigator all the same props (the same in App1Component)
Here is App1Component with a button to push next scene to App2Component and pass the same props, before binded through Container.
class App1Component extends Component {
render() {
return (
<View>
<MainContent {...this.props}/>
<TouchableOpacity onPress={()=>{
this.props.navigator.push({
name: 'App2',
component: App2Component,
passProps: {...this.props}
})
}}>
<Text>Click to Forward to App2Component {'\n'}passing props through Navigator passProps</Text>
</TouchableOpacity>
</View>
)
}
}
If you press 'enlarge' here the string is enlarged, and you can see the action reflected on the screen. Also you can see in chrome the trace for redux logger.
Here the wrapper container for App1Component
const Container = connect(
(state) => ({
data: state.data
}),
(dispatch) => ({
enlarge: () => dispatch(enlarge())
})
)(App1Component)
And Here Component 2, with back button.
If you press 'enlarge' here the string is enlarged, sure you can see the trace on chrome, but ... the action is not reflected on the screen.
class App2Component extends Component {
render() {
return (
<View>
<MainContent {...this.props}/>
<TouchableOpacity onPress={()=>{this.props.navigator.pop()}} >
<Text>Back to App1Component{'\n'}to see the change!</Text>
</TouchableOpacity>
</View>
)
}
}
After pressing Back, you can see the string modified on screen for App1Component.
As you can see I misunderstood something fundamental, but not what is.
Have i to decorate all the mains components (Pages/Scenes) with a container?
Why is wrong to pass props (previously binded by container) through passPros in Navigator? Actions are dispatched correctly, but does not reflect on the screen.
Any help is welcome.
Thanks
It looks like passProps does not propagate updates correctly. I’m not a React Native expert so I can’t confirm it, but I would definitely recommend you to use connect() for subscribing components to Redux store rather than passing props from the top.

Resources