Changing segment content onPress - react-native

I have segments and I want them to act as tabs. I tried using react-navigation onPress of each segment but it doesn't look nice.
I want the segments to stay fixed at the top and on clicking each one the content should change or call a specific component without reloading or noticing that the screen has changed.
<Segment>
<Button first>
<Text>Puppies</Text>
</Button>
<Button>
<Text>Kittens</Text>
</Button>
<Button last active>
<Text>Cubs</Text>
</Button>
</Segment>
<Content>
<Text>Awesome segment</Text>
</Content>

Segments can be customized as a react-navigation single screen separated into multiple components which can be rendered on demand.
Example
(Using native-base components)
state = {
activePage:1,
}
selectComponent = (activePage) => () => this.setState({activePage})
_renderComponent = () => {
if(this.state.activePage === 1)
return <Component1/> //... Your Component 1 to display
else
return <Component2/> //... Your Component 2 to display
}
render() {
return (
<Container>
<Header>
<Left />
<Body>
<Segment>
<Button active={this.state.activePage === 1}
onPress={this.selectComponent(1)}><Text>Component 1</Text></Button>
<Button active={this.state.activePage === 2}
onPress= {this.selectComponent(2)}><Text>Component 2</Text></Button>
</Segment>
</Body>
<Right/>
</Header>
<Content padder>
{this._renderComponent()}
</Content>
</Container>
)
}
You can customize it to add more components based on the conditions set above
Edit
Supposing you navigate to a particular screen that also contains Segment, the on some item button click you can do
this.props.navigation.navigate('OtherSegmentScreen', {activePage: this.state.activePage})
And on that page receive the props as
const {navigate} = this.props.navigation
const previousActiveSegment = navigate.getParams('activePage', '1') //... 1 is default value that you can set

Related

How to change state of react hook inside other component

Senario
I have a dialog, it look something like this, it have hook for showing this dialog, called showDialog , and dialog have a button with Onpress method
export default function DialogTesting(show: boolean) {
const [showDialog, doShow] = useState(show)
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}
>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={showDialog}
title="Record New Progress"
style={DIALOG}
onClose={() => {
doShow(false)
}}
>
And a main sceen , it also have hook to show dialog, called show,
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false)
function hanndleDialog() {
setShow(!show)
}
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show}
callback={() => {
hanndleDialog()
}}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
Problem
When i click the button in main screen, change hook show to true to show dialog, and use this hook state in dialog to set show in dialog to true to show that dialog, and in dialog, when i click button to close dialog, it disappear, but problem is, the state of show in main screen remain true, so i have to press twice to show dialog again
Question
How can i change hook status in main screen, or how can i press close button in dialog, the show hook in main screen return false, i tried to change state of mainscreen in dialog but it won't work
Here is a short video of problem
https://streamable.com/9mm26t
You should maintain just one copy of your state in the parent component itself. Then pass "show" and "setShow" as props to the child component.
Parent Component:
export const MedicalRecord = memo(function MedicalRecord() {
// const onPressViewAll = useCallback(() => {}, [])
const [show, setShow] = useState(false)
function hanndleDialog() {
setShow(!show)
}
return (
<SummaryViewContainer
count={5}
title={"dashboardScreen.medicalRecords.title"}
onPress={() => {
hanndleDialog()
}}
>
<View>
{show && (
<ProgressDialog
show={show}
setShow = {setShow}
/>
)}
<RecordItem />
<RecordItem />
<RecordItem />
</View>
</SummaryViewContainer>
)
})
Dialog Component:
export default function DialogTesting({show, setShow}) {
return (
<View>
{/* <Button
title="click"
onPress={() => {
setShow(true)
}}>
<Text>Show dialog</Text>
</Button> */}
<Dialog
visible={show}
title="Record New Progress"
style={DIALOG}
onClose={() => {
setShow(false)
}}
>
</View>
)
}
It looks like you have 2 versions of local state at each component. You need to keep 1 version of "show" at the top most parent where you care to control this variable and then pass it down as a prop to your child components. Then on your child components you need to expose a callback that the parent will pass down and the child will call to trigger what occurs when the button is clicked in the child component.

How to pass state into Parent?

I am fairly new to React, and I am making a small single page application, and I am trying to figure out how to pass the employee ID to the parent.
So there is an issue here, which I already know.... but trying to figure out a solution.
There will be 3 screens first starts with the ID.
When I have it as the code listed below, everytime I type, the state rerenders, however it DOES work. It just really annoying to type.
import React, { useState } from 'react';
import {
Container,
Header,
Title,
Body,
Content,
Item,
Input,
Icon,
Text,
View,
} from 'native-base';
export default function App() {
const [id, setId] = useState('');
const IdScreen = () => {
return (
<View>
<Text>Enter your Employee ID</Text>
<Item rounded style={styles.textBox}>
<Icon type='Ionicons' name='person' style={{ fontSize: 30 }} />
<Input
onChangeText={(text) => {
setId(text);
}}
value={id}
/>
</Item>
</View>
);
};
return (
<Container>
<Header>
<Body>
<Title>Employee Enroll</Title>
</Body>
</Header>
<Content>
<IdScreen />
</Content>
</Container>
);
}
If I make this change... the typing part is fine, but when I go to push this to the API, it wont be readable.
const IdScreen = () => {
const [id, setId] = useState('');
return (
<View>
<Text>Enter your Employee ID</Text>
<Item rounded style={styles.textBox}>
<Icon type='Ionicons' name='person' style={{ fontSize: 30 }} />
<Input
onChangeText={(text) => {
setId(text);
}}
value={id}
/>
</Item>
</View>
);
};
I am looking for a way to type in the person ID, then save that state to the main function so that I can use that variable later.
I just don't know how to do that.
First of all take the IdScreen component out of the App component (You should have your components in different files), then if you want to pass the id from IdScreen to App you need to lift up the state. In order to do that you will need to the following:
Put this const [id, setId] = useState(''); only in the App component, then pass the setId function to the IdScreen component as a props, like this:
<IdScreen id={id} setId={setId} />
Then in your IdScreen component you can use the setter function and the value as props, like this:
props.id and props.setId

React-Native: Element type is invalid

I have problem with nativebase Footer
I have Container and if I include MyFooter, it give me this error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in.
// main.js
import MyFooter from './MyFooter';
...
<Container>
<MyHeader title="Оплаты" />
<Content></Content>
<MyFooter />
</Container>
And Footer component
// MyFooter.js
const MyFooter = props => {
return (
<Footer>
<FooterTab>
<Button vertical active>
<Text>Info</Text>
</Button>
<Button vertical >
<Text>Remove</Text>
</Button>
</FooterTab>
</Footer>
);
}
export default MyFooter;
But if I change render method of MyFooter like this:
// MyFooter.js
return (
<View>
<Text>
Test
</Text>
</View>
)
So problem not in export/import, because with another render in MyFooter all work perfectly.
Can anybody help with this, please?
Answer - import { Text, Footer, FooterTab, Button, Icon } from 'react-native'; ('react-native' instead 'native-base')
Is this your MyFooter component try to export your component first export default MyFooter like these following:
const MyFooter = () => (
<Footer>
<FooterTab>
<Button vertical active>
<Icon name="information" />
<Text>Инфо</Text>
</Button>
<Button vertical >
<Icon name="add" />
<Text>Оплаты</Text>
</Button>
<Button vertical >
<Icon name="remove" />
<Text>Снятия</Text>
</Button>
</FooterTab>
</Footer>
);
export default MyFooter;
If you pasted your code exactly as is, then you're missing your closing brace after your return statement in MyFooter.js

Adjacent JSX elements must be wrapped in an enclosing tag

I want to have a navigation bar at the bottom and a toolbar at the top of every page in my React-Native app. However, if I create these two elements in the index.ios.js file, I get the error: "Adjacent JSX elements must be wrapped in an enclosing tag". If I put tags surrounding the Navigator and NavBar I just see a blank screen in my app. What should I do? Here is what my render function looks like in index.ios.js
render() {
return (
<Navigator
initialRoute={{name: 'Login'}}
renderScene={this.renderScene}
navigationBar={
<Navigator.NavigationBar
style={ styles.nav }
routeMapper={ NavigationBarRouteMapper } />
}
/>
<NavBar />
);
}
As per React 16, if you wan't to avoid the <View> wrapping,
you can return multiple components from render as an array.
return ([
<Navigator key="navigator" />,
<NavBar key="navbar" />
]);
Or in a stateless ES6 component:
import React from 'react';
const Component = () => [
<Navigator key="navigator" />,
<NavBar key="navbar" />
];
export default Component;
Don't forget the key property as React needs (as for iteration on Array) to be able to discriminate your components.
Edit (Nov. 2018)
Your can also use React.Fragment shorthand:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
When you wrap it in a view make sure you set the flex to 1. My guess is that the view you are giving it has no size and therefore the child elements are inheriting their size from the parent (which is 0)
Wrap both inside a View and give that outer View wrapper a style of flex 1. Example:
<View style={{flex: 1}}>
<Navigator
{. . .}
/>
<NavBar />
</View>

Implementing Footer Tabs in Native React using Native Base

I am creating a native react application using native base for the UI (http://nativebase.io/docs/v2.0.0/components#footerTab). I am using the footerTabs component and my code is as follows
render() {
return (
<Container>
<Header backgroundColor="#ECEFF1">
<Button transparent>
<Icon name='ios-menu' style={{color: 'black'}}/>
</Button>
<Title style={{color:'black'}}>Header</Title>
</Header>
<Content>
<Profile/>
</Content>
<Footer backgroundColor="#212121">
<FooterTab>
<Button backgroundColor="#000" >
<Icon name="ios-person" size={30} color="#900"/>
<Text>Profile</Text>
</Button>
<Button>
<Icon name="ios-search"/>
<Text>Search</Text>
</Button>
<Button>
<Icon name="ios-camera"/>
<Text>Camera</Text>
</Button>
<Button>
<Icon name="ios-apps"/>
<Text>Apps</Text>
</Button>
<Button>
<Icon active name="ios-navigate"/>
<Text>Navigate</Text>
</Button>
</FooterTab>
</Footer>
</Container>
);
}
I have created different JS files for different functionalities such as Profiles,Search,Apps ect.. and have imported them as follows.
import Profile from './Profile';
How do I implement the onPress functionality on the buttons of the footer to change the component in the content tag depending on what was selected?
<Content>
<Profile/>
</Content>
For eg: If I pressed the search button I want the profile tag to be replaced with and similarly the same for the other buttons.
Here FooterTab from native base is not persist actual tab functionality like UITabBar in iOS, its only persist for design. What you need to do is, keep footer tag in all of your component with all four buttons and keep active accordingly. For changing view by selecting any button you need to replace current view by selected one using navigator like:
<Button onPress={()=> { this.props.navigator.replace({id:'component name'}) }}>
and in your navigator component you should define all required components in renderScene method on the basis of id value in route payload. If you want actual functionality as TabBar work then you can use this third party module react-native-tab-navigator. Thanks!
Instead of replacing the content, why don't you have each Button navigate to a new page?
Let's say you're on the Profile tab. You could do something like this:
import FooterWrapper from './FooterWrapper'
<Footer>
<FooterWrapper tab='profile' navigator={this.props.navigator} />
</Footer>
And then in your FooterWrapper (I just handled a case of two tabs):
constructor(props) {
super(props)
this.state = {
profileTab: this.props.tab === 'profile',
searchTab: this.props.tab === 'search',
}
}
navToProfilePage() {
this.props.navigator.push({
id: 'profile',
tab: 'profile',
})
}
navToSearchPage() {
this.props.navigator.push({
id: 'search',
tab: 'search',
})
}
render() {
return (
<FooterTab>
<Button active={this.state.profileTab} onPress={() => this.navToProfilePage()}>
Profile
<Icon name='ios-person' size={30} color='#900' />
</Button>
<Button active={this.state.searchTab} onPress={() => this.navToSearchPage()}>
Search
<Icon name='ios-search' />
</Button>
</FooterTab>
)
}
Ok so here is I how got it I used the renderContent method within the content tags to generate views depending on the state change when the button was clicked.
<Content>
{this._renderContent(this.state.selectedTab)}
</Content>
The selectedTab is a state variable whose state is set using this.setState in the onPress method. The renderContent has an if function that checks the selected tab and returns the appropriate view. I also tried the navigation approach but this seemed cleaner.
_renderContent = (Tab: string,) => {
if(this.state.selectedTab==="Profile"){
return (
<Profile/>
);
}
else if(this.state.selectedTab==="Search"){
}
}