I have Tab navigator that handles data changing of itself and other two sibling component.
Main parent Component that does data fetching and manipulation based on three sentiments: positive, negative and neutral as request body parameter in http post request.
Second parent component that stores all positive data, negative data and neutral data from main parent component.
Then three Child Components: Table one and Tab Navigation.
The Tab navigation has three three tabs namely: Positive, Negative and Neutral.
As soon as the page renders on first load, by default (without clicking positive tab button), it fetches positive data and displays it in all three child components. Then on clicking on Negative tab, it should display negative data in all three child components i.e. Table one and under negative tab of tab navigation. Same thing follows for Neutral Tab.
In short, Tab navigation handles data rendering of it's self and sibling components based on its active state and getting that data from parent component.
I tried passing the active event from tab navigation to its upper container but it doesn't seem to work fine.
Tab.js
import React, { Component } from 'react';
export default class Tab extends Component
{
constructor(props)
{
super(props);
this.state =
{
active: 'positive',
}
}
toggle = (event) =>
{
this.setState({
active: event
})
this.props.sendEvent(this.state.active);
}
get_tab_content =()=>
{
switch(this.state.active)
{
case 'positive':
return <div><SomeDataComponent1 positiveprops={} /></div>;
case 'negative':
return <div><SomeDataComponent2 negativeprops={}/></div>;
case 'neutral':
return <div><SomeDataComponent3 neutralprops={} /></div>;
default :
}
}
render() {
const tabStyles = {
display: 'flex',
justifyContent: 'center',
listStyleType: 'none',
cursor: 'pointer',
width: '100px',
padding: 5,
margin: 4,
fontSize: 20,
color: 'green'
}
const tabStyles2 = {
display: 'flex',
justifyContent: 'center',
listStyleType: 'none',
cursor: 'pointer',
width: '100px',
padding: 5,
margin: 4,
fontSize: 20,
color: 'red'
}
const tabStyles3 = {
display: 'flex',
justifyContent: 'center',
listStyleType: 'none',
cursor: 'pointer',
width: '100px',
padding: 5,
margin: 4,
fontSize: 20,
color: 'yellow'
}
const linkStyles = {
display: 'flex',
justifyContent: 'center',
color: '#000',
listStyleType: 'none'
}
const divStyle = {
border: '1px solid #34baa2',
width: '450px'
}
const box = this.get_tab_content()
return (
<div style={divStyle} >
<ul style={linkStyles}>
<li style={tabStyles} onClick={function(){this.toggle('positive')}.bind(this)}>Positive</li>
<li style={tabStyles2} onClick={function(){this.toggle('negative')}.bind(this)}>Negative</li>
<li style={tabStyles3} onClick={function(){this.toggle('neutral')}.bind(this)}>Neutral</li>
</ul>
<div>
{box}
</div>
</div>
);
}
}
Second Parent Component.js
import React,{Component} from 'react';
import Tab from '../tab/tab';
import MentionTable from '../table/table';
class DataCharts extends Component{
constructor(props){
super(props);
this.state = {
childEvent: ''
}
}
getEvent = (childevent) => {
this.setState({
childEvent: childevent
});
console.log(this.state.childEvent)
}
render(){
const {positivetable,positivewords, negativetable, negativewords, neutraltable, neutralwords } = this.props;
return(
<div style={{display:'flex', flexDirection: 'row'}}>
<Table />
<Tab sendEvent={this.getEvent}/>
</div>
)
}
}
export default DataCharts;
The problem with your code is the next lines:
toggle = event => {
this.setState({
active: event
});
this.props.sendEvent(this.state.active);
};
setState is asynchronous, so you sending to sendEvent the not intended state.
toggle = event => {
this.setState(prevState => {
console.log('prevState', prevState);
console.log(event);
this.props.sendEvent(event);
return { active: event };
});
};
I think calling the parent function in setState callback would work, for thet we need to do it like this in Tab.js
this.setState({
active: event
}, () => { this.props.sendEvent(this.state.active)})
also console.log(this.state.childEvent) should be also in callback too so that you get the state after it has been updated, we need to change DataCharts.js too
this.setState({
childEvent: childevent
}, () => { console.log(this.state.childEvent)})
Working demo -: https://stackblitz.com/edit/react-zhbmad
Related
I am making a basic drop down menu in React. I am new to React and have managed to get to where the menu opens on click and such. What I would like to do is change the background of each choice to a different color only when the mouse is hovered over that specific element.
Right now the issue is that every element is highlighted when one is hovered over. I understand this is because I am changing the state which effects every element, but I cant wrap my mind around how to implement it how I would like it. Perhaps there is a React concept(s) I am unaware of that would make this easier.
Here is my code so far:
App.jsx file
import React from "react";
import InnerDiv from './components/InnerDiv';
import CountiesList from "./components/countiesList";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
menuIsOpen: false,
height: '50px'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick = () => {
if (this.state.menuIsOpen == false) {
this.setState(state => ({
menuIsOpen: !state.menuIsOpen,
height: '350px'
}))
} else {
this.setState(state => ({
menuIsOpen: !state.menuIsOpen,
height: '50px'
}))
}
}
render() {
const divStyle = {
height: this.state.height,
width: '300px',
border: '2px solid cornflowerblue',
borderRadius: '12px',
position: 'absolute',
top: '50px',
left: '50px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'center',
transition: 'all 0.1s linear',
}
return (
<div onClick={this.handleClick} style={divStyle} className="App">
<InnerDiv/>
<CountiesList/>
</div>
);
}
}
export default App;
InnerDiv.jsx file
import React from "react";
const innerDivStyle = {
height: '50px',
width: '90%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}
const h1Style = {
fontSize: '20px',
WebkitTouchCallout: 'none',
WebkitUserSelect: 'none',
khtmlUserSelect: 'none',
MozUserSelect: 'none',
msUserSelect: 'none',
UserSelect: 'none'
}
const svgStyle = {
height: '30px'
}
class InnerDiv extends React.Component {
constructor(props) {
super(props);
}
render(){
return (
<div style={innerDivStyle} className="InnerDiv">
<h1 style={h1Style}>Select A Location</h1>
<svg style={svgStyle} xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 122.88 122.88"><title>round-line-bottom-arrow</title><path d="M122.85,61.45h0A61.39,61.39,0,0,0,61.45,0V0h0V0A61.38,61.38,0,0,0,0,61.43H0v0H0a61.35,61.35,0,0,0,61.4,61.38v0h0v0a61.34,61.34,0,0,0,61.38-61.4ZM61.44,91,33.92,60.47H51.5V39.2H71.38V60.47H89L61.44,91Zm48.28-29.54h0a48.36,48.36,0,0,1-48.27,48.29v0h0v0A48.35,48.35,0,0,1,13.14,61.47h0v0h0A48.27,48.27,0,0,1,61.41,13.14v0h0v0a48.3,48.3,0,0,1,48.27,48.3Z"/></svg>
</div>
)
}
}
export default InnerDiv;
countiesList.jsx file
import React from "react";
class CountiesList extends React.Component {
constructor(props) {
super(props);
this.state = {
counties: [
'Antrim',
'Armagh',
'Carlow',
'Cavan',
'Clare',
'Cork',
'Derry',
'Donegal',
'Down',
'Dublin',
'Fermanagh',
'Galway',
'Kerry',
'Kildare',
'Kilkenny',
'Laois',
'Leitrim',
'Limerick',
'Longford',
'Louth',
'Mayo',
'Meath',
'Monaghan',
'Offaly',
'Roscommon',
'Sligo',
'Tipperary',
'Tyrone',
'Waterford',
'Westmeath',
'Wexford',
'Wicklow'
],
backgroundColor: '#a9c4f5'
};
this.updateBackgroundColor = this.updateBackgroundColor.bind(this);
this.reverseBackgroundColor = this.reverseBackgroundColor.bind(this);
}
updateBackgroundColor() {
this.setState({backgroundColor: 'cornflowerblue'})
}
reverseBackgroundColor() {
this.setState({backgroundColor: '#a9c4f5'})
}
render() {
const ulStyle = {
listStyleType: 'none',
paddingInlineStart: 0,
margin: 0,
width: '100%',
height: '300px',
overflowY: 'scroll',
borderBottomLeftRadius: '12px'
}
const liItemContainer = {
height: '50px',
paddingLeft: '15px',
display: 'flex',
alignItems: 'center',
background: this.state.backgroundColor,
}
const liStyle = {
fontWeight: '700'
}
let countiesListItems = this.state.counties.map(county => {
return (
<div key={county} style={liItemContainer} onMouseEnter={this.updateBackgroundColor} onMouseOut={this.reverseBackgroundColor}>
<li style={liStyle}>{county}</li>
</div>
)
})
return (
<ul style={ulStyle}>
{countiesListItems}
</ul>
)
}
}
export default CountiesList;
Thank you for your help in advance
I'm trying to make a custom header with react navigation but i can't get proper resoult. I want red view to cover all of the blue one but I can't figure it out.
Here is a picture
My HomeStack.js:
const screens = {
Home: {
screen: HomeScreen,
}
}
const options = {
defaultNavigationOptions: {
headerTitle: () => <Header/>,
headerStyle: {
backgroundColor: 'lightblue',
height: 60,
}
}
}
const HomeStack = createStackNavigator(screens, options)
const Navigator = createAppContainer(HomeStack)
export default Navigator
My Header.js:
class Header extends Component {
render() {
return(
<View style={style.conteiner}>
<Text style={style.text}>Text</Text>
</View>
)
}
}
const style = StyleSheet.create({
conteiner: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'red',
},
text: {
color: '#fff',
fontSize: 20,
}
})
export default Header
If I understand you correctly, then you want to define a custom component for the entire header. You are using your custom header component to set headerTitle:
const options = {
defaultNavigationOptions: {
headerTitle: () => <Header/>, <-- here
headerStyle: {
backgroundColor: 'lightblue',
height: 60,
}
This is what the documentation says about headerTitle:
Sometimes you need more control than just changing the text and styles
of your title -- for example, you may want to render an image in place
of the title, or make the title into a button. In these cases you can
completely override the component used for the title and provide your
own.
This option is only intended for replacing the title component, not the entire header. Unless you have some very specific requirements, you can likely achieve what you are looking for using a combination of headerStyle, headerTintColor and headerTitle.
You need to pass your custom header component to the header props for the stack navigator.
headerTitle is for the title inside the header component.
https://reactnavigation.org/docs/4.x/stack-navigator#header
https://reactnavigation.org/docs/4.x/stack-navigator#headertitle
https://reactnavigation.org/docs/4.x/stack-navigator
Cannot solve that one.
I'm creating navigation bar with a scroll-to functionality, ie. when pressed on the certain menu item the page is scrolled to the corresponding section. While this seems to be quite straightforward I don't know how to highlight in a different colour this certain menu item when window gets to the point of the section. F.ex. user presses 'contact', the page is scrolled to contact section, the contact menu item changes its colour to red. Thank you for any help!
Here's my code:
import React, { Component } from 'react';
import Container from './Container.jsx';
class Header extends Component {
constructor(props) {
super(props);
this.state = {
backgroundColor: 'rgba(34,34,34,0)',
};
}
componentDidMount() {
window.addEventListener('scroll', () => this.handleScroll());
}
handleScroll(e) {
const test = (pageYOffset > 900) ?
(this.setState({ backgroundColor: 'black' })) :
(this.setState({ backgroundColor: 'rgba(34,34,34,0)' }));
}
handleClick(e) {
e.preventDefault();
const elementOffsetTop = document.getElementById(e.target.innerText).offsetTop;
window.scrollTo(0, elementOffsetTop);
}
handleUp(e) {
e.preventDefault();
window.scrollTo(0, 0);
}
render() {
const menuItems = [
{ menuItem: 'O nas', link: 'About' },
{ menuItem: 'Księgowość', link: 'Ksiegowosc' },
{ menuItem: 'Kadre i płace', link: 'Kadre' },
{ menuItem: 'Doradztwo', link: 'Doradztwo' },
{ menuItem: 'Nieruchomości', link: 'nieruchomosci' },
{ menuItem: 'Kontakt', link: 'kontakt' }
];
const items = menuItems.map(item => {
const styles = {
linkStyle: {
textDecoration: 'none',
color: '#ffffff',
cursor: 'pointer'
},
textStyle: {
marginLeft: '1rem',
textTransform: 'uppercase'
}
};
const { linkStyle, textStyle } = styles;
return (
<a onClick={e => this.handleClick(e)} key={item.link} style={linkStyle}>
<p style={textStyle}> {item.menuItem} </p>
</a>
);
});
const styles = {
containerStyle: {
height: 70,
position: 'fixed',
top: 0,
width: '100%',
backgroundColor: this.state.backgroundColor,
zIndex: 20000,
},
headerStyle: {
height: 70,
},
navigationStyle: {
height: '100%',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
fontSize: '0.9rem'
},
navigationItemsStyle: {
display: 'flex',
},
logoStyle: {
fontSize: '1.3rem',
cursor: 'pointer'
}
};
const { headerStyle,
containerStyle,
navigationStyle,
navigationItemsStyle,
logoStyle
} = styles;
return (
<div id="header" style={containerStyle}>
<header style={headerStyle} ref='header'>
<Container>
<div style={navigationStyle}>
<a onClick={e => this.handleUp(e)} style={logoStyle}>
<div>{this.props.text}</div>
</a>
<div style={navigationItemsStyle}> {items} </div>
</div>
</Container>
</header>
</div>
);
}
}
export default Header;
you can use react-intersection-observer to achieve that.
More info about that on react-intersection-observer
I am working on a react native app that uses Composite Experimental Navigation (CardStack + Tabs) and redux for state management. I was able to create Tab based Navigation but the issue that i am facing now is when i switch between tabs the component is unmounted and it re-render every time.
Problems
Lets say i have scrolled down several posts and when i change Tab it will start from top. (Workaround could be to store scroll position in redux state).
Here is the sample code i am using for Navigation Tabbed Experimental Navigation
You must change approach for TabBar.
Basically you want Navigator per Tab, so you can have route stack for each Tab and one Navigator to contain Tabs (TabBar). You also want to set initial route stack for TabBar and jump between those routes.
It is important to understand difference between Navigator methods.
When you pop route, it's unmounted, and active index is moved to
last one. As last one is kept in state it will be restored as it was
previously rendered (most likely, it can happen props changed). Going again to popped route will rerender scene entirely (this happens to you).
When you push route, nothing is unmounted, new route is mounted.
When you jumpToIndex route, again nothing in unmounted, thus jumping
between routes restores scenes as they were (again, if props changed
scene will be rerendered).
So, I don't think this is correct:
I was able to create Tab based Navigation but the issue that i am facing now is when i switch between tabs the component is unmounted and it re-render every time.
... you unmount routes with wrong navigation actions.
Also what is different now, NavigationCardStack doesn't actual create it's own state, it is passed from outside, which gives you great flexibility. And also good thing is that you can use reducers provided by Facebook for common actions (such as push, pop, jumpToIndex; they are part of Navigation Utils).
You have full example on how to create navigationState and it reducers here, so I am not going to explain that, just going to give idea how to solve your problem.
[UPDATE] Example works now!
import React from 'react';
import { NavigationExperimental, View, Text, StyleSheet } from 'react-native';
const {
CardStack: NavigationCardStack,
StateUtils: NavigationStateUtils,
} = NavigationExperimental;
const style = StyleSheet.create({
screen: {
flex: 1,
},
screenTitle: {
marginTop: 50,
fontSize: 18,
},
pushNewScreenLabel: {
marginVertical: 10,
fontSize: 15,
fontWeight: "bold",
},
goBackLabel: {
fontSize: 15,
},
tabBarWrapper: {
position: 'absolute',
height: 50,
bottom: 0,
left: 0,
right: 0,
top: null,
backgroundColor: 'grey',
flexDirection: 'row',
flex: 0,
alignItems: 'stretch',
},
tabBarItem: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'red',
},
});
export class TabBar extends React.Component {
constructor(props, context) {
super(props, context);
this.jumpToTab = this.jumpToTab.bind(this);
// Create state
this.state = {
navigationState: {
// Active route, will be rendered as default
index: 0,
// "tab-s" represents route objects
routes: [
{ name: 'Tab1', key: '1' },
{ name: 'Tab2', key: '2' },
{ name: 'Tab3', key: '3' },
{ name: 'Tab4', key: '4' }],
},
};
}
jumpToTab(tabIndex) {
const navigationState = NavigationStateUtils.jumpToIndex(this.state.navigationState, tabIndex);
this.setState({ navigationState });
}
renderScene({ scene }) {
return <Tab tab={scene.route} />;
}
render() {
const { navigationState } = this.state;
return (
<View style={style.screen}>
<NavigationCardStack
onNavigate={() => {}}
navigationState={navigationState}
renderScene={this.renderScene}
/>
<View style={style.tabBarWrapper}>
{navigationState.routes.map((route, index) => (
<TabBarItem
key={index}
onPress={this.jumpToTab}
title={route.name}
index={index}
/>
))}
</View>
</View>
);
}
}
class TabBarItem extends React.Component {
static propTypes = {
title: React.PropTypes.string,
onPress: React.PropTypes.func,
index: React.PropTypes.number,
}
constructor(props, context) {
super(props, context);
this.onPress = this.onPress.bind(this);
}
onPress() {
this.props.onPress(this.props.index);
}
render() {
return (
<Text style={style.tabBarItem} onPress={this.onPress}>
{this.props.title}
</Text>);
}
}
class Tab extends React.Component {
static propTypes = {
tab: React.PropTypes.object,
}
constructor(props, context) {
super(props, context);
this.goBack = this.goBack.bind(this);
this.pushRoute = this.pushRoute.bind(this);
this.renderScene = this.renderScene.bind(this);
this.state = {
navigationState: {
index: 0,
routes: [{ key: '0' }],
},
};
}
// As in TabBar use NavigationUtils for this 2 methods
goBack() {
const navigationState = NavigationStateUtils.pop(this.state.navigationState);
this.setState({ navigationState });
}
pushRoute(route) {
const navigationState = NavigationStateUtils.push(this.state.navigationState, route);
this.setState({ navigationState });
}
renderScene({ scene }) {
return (
<Screen
goBack={this.goBack}
goTo={this.pushRoute}
tab={this.props.tab}
screenKey={scene.route.key}
/>
);
}
render() {
return (
<NavigationCardStack
onNavigate={() => {}}
navigationState={this.state.navigationState}
renderScene={this.renderScene}
/>
);
}
}
class Screen extends React.Component {
static propTypes = {
goTo: React.PropTypes.func,
goBack: React.PropTypes.func,
screenKey: React.PropTypes.string,
tab: React.PropTypes.object,
}
constructor(props, context) {
super(props, context);
this.nextScreen = this.nextScreen.bind(this);
}
nextScreen() {
const { goTo, screenKey } = this.props;
goTo({ key: `${parseInt(screenKey) + 1}` });
}
render() {
const { tab, goBack, screenKey } = this.props;
return (
<View style={style.screen}>
<Text style={style.screenTitle}>
{`Tab ${tab.key} - Screen ${screenKey}`}
</Text>
<Text style={style.pushNewScreenLabel} onPress={this.nextScreen}>
Push Screen into this Tab
</Text>
<Text style={style.goBackLabel} onPress={goBack}>
Go back
</Text>
</View>
);
}
}
## TBD little more clean up..
I want to add a functionality to aa button where long press results in opening of a popup where user can choose from multiple options. I wanted to know if there is any component available that renders that prop because AFAIK onLongPress of TouchableHighlight(or Opacity)is non renderable. I understand I can change the state and display another view but I want to make the menu transparent so that clicking(or tapping) on the background results in disappearance of the menu.
The react-native-popover project looks like it could be an option for you. A note of caution is that I'm not sure how actively maintained it is at the moment. For example, the current version is 0.3.0 but only 0.2.0 is released to npm. To correct that in the interim, see this issue.
At a minimum, you can at least review this code as it accomplishes what I believe you're after. Here is a code sample extended off the project site that creates a popover component on a button with a transparent background. When the background is tapped, the popover closes.
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import Popover from 'react-native-popover';
class MyApp extends React.Component {
constructor (props) {
super(props);
this.onLongPress = this.onLongPress.bind(this);
this.onClose = this.onClose.bind(this);
this.state = {
isVisible: false,
buttonRect: {}
}
}
onLongPress () {
this._button.measure((ox, oy, width, height, px, py) => {
this.setState({
isVisible: true,
buttonRect: {x: px, y: py, width: width, height: height}
});
});
}
onClose () {
this.setState({ isVisible: false });
}
render () {
return (
<View style={styles.container}>
<TouchableOpacity
ref={(component) => this._button = component}
style={styles.button}
onLongPress={this.onLongPress}>
<Text>Long Press Me</Text>
</TouchableOpacity>
<Popover
isVisible={this.state.isVisible}
fromRect={this.state.buttonRect}
onClose={this.onClose}
backgroundStyle={styles.popoverBackground}>
<Text>I'm the content of this popover!</Text>
</Popover>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
button: {
backgroundColor: '#ddd',
padding: 20
},
popoverBackground: {
backgroundColor: 'rgba(0,0,0,0)'
}
});
AppRegistry.registerComponent('MyApp', () => MyApp);