React-Native-Navigation changing state in other screen with getParam() - react-native

I want to navigate to Decrease screen from Home screen and after entering a number into TextInput send it to change total in Home screen by using ```getParam('clickMe','changeTotal')`` but I couldn't find a way to pass number to Home screen.
Home.Js
export default class Home extends Component {
state = {
total: 1000
};
decreaseTotalHandlerNavigate = () => {
this.props.navigation.navigate("Decrease", { clickMe: this.changeTotal });
};
changeTotal = number => {
this.setState(prevState => ({ total: prevState.total - number }));
};
render() {
return (
<View>
<Text>{this.state.total}</Text>
<TouchableOpacity onPress={this.decreaseTotalHandlerNavigate}>
<Text>Decrease</Text>
</TouchableOpacity>
</View>
);
}
}
Decrease.js
export default class Decrease extends Component {
state = {
number: 0
};
render() {
return (
<View>
<TextInput
onChangeText={value => {
this.setState({
number: value
});
}}
/>
<TouchableOpacity onPress={this.props.navigation.getParam("clickMe", "changeTotal")}>
<Text>Add</Text>
</TouchableOpacity>
</View>
);
}
}

Related

How to skip back/modify navigation

I have a stackNavigation which works great. 3 screens : DeviceList / DeviceDetail / DevideAdd. The normal paths is (1) DeviceList > DeviceDetail or (2) DeviceList > DeviceAdd > DeviceDetail.
But when user use the path (2), I want that the back button of the DeviceDetail screen go to DeviceList. Or for the moment, it's going to DevideAdd.
Here is my navigation:
const DeviceStackNavigator = createStackNavigator({
DeviceList: {
screen: DeviceList,
},
DeviceDetail: {
screen: DeviceDetail,
},
DeviceAdd: {
screen: DeviceAdd,
},
});
How to achieve it ?
The key attribute 'goBack()' is a dynamically generated string that is created each time you navigate to a new 'react-navigation' path.
So if you want to go from DeviceDetail to DeviceList, what you have to do is to pass the key of DeviceAdd down to DeviceDetail, and then call goBack() with the key.
DeviceAdd.js
render() {
const { state, navigate } = this.props.navigation;
return (
<View>
<Button title="Go to DeviceDetail" onPress={ () => {
navigate('DeviceDetail', { go_back_key: state.key });
}} />
</View>
);
}
DeviceDetail.js
render() {
const { state, goBack } = this.props.navigation;
const params = state.params || {};
return (
<View>
<Button title="Back to DeviceList" onPress={ () => {
goBack(params.go_back_key);
}} />
</View>
);
}

Redux reducer not changing prop

I am making a todo list application with redux. I am able to add todos perfectly fine with redux however my toggle todos and remove todos are having problems.
The toggle todo action gets called by the redux store (I see it happening in the debugger), however, it does not update the prop to be the opposite of completed and I am not sure why.
I have tried playing around with the syntax and modeling other people's redux todo lists for hours but have not been able to solve this issue.
My toggleTodo and removeTodo actions:
export const toggleTodo = (item) => {
return {
type: TOGGLE_TODO,
id: item.id
};
};
export const removeTodo = (item) => {
return {
type: REMOVE_TODO,
id: item.id
};
};
My TodoReducer: // this is where I suspect the problem is
const initialState = {
todos: []
};
const todos = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_TODO:
if (state.id !== action.id) {
return state;
}
return {
...state, completed: !state.todos.completed
};
case REMOVE_TODO: {
const newState = [...state];
newState.splice(action.id, 1);
return { ...newState };
}
My main flatlist where I call the actions:
render() {
return (
<View style={{ height: HEIGHT }}>
<FlatList
data={this.props.todos}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem
todoItem={item}
pressToToggle={() => this.props.toggleTodo(item)}
deleteTodo={() => this.props.removeTodo(item)}
/>
);
}}
/>
</View>
);
}
}
export default connect(mapStateToProps, { addTodo, toggleTodo, removeTodo })(MainTodo);
// I call the actions I am using here and don't use mapDispatchToProps
And my TodoItem component where I pass in the props:
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity
style={styles.todoItem}
onPress={this.props.pressToToggle}
>
<Text
style={{
color: todoItem.completed ? '#aaaaaa' : '#f5f5f5',
textDecorationLine: todoItem.completed ? 'line-through' : 'none',
fontSize: 16 }}
>
{todoItem.text}
</Text>
<Button
title='Remove'
color='#ff5330'
onPress={this.props.deleteTodo}
/>
</TouchableOpacity>
</View>
);
}
}
When I hit toggle todo instead of the prop changing and the line coming through over the text nothing happens.
And when I try to remove a todo I get this error- "invalid attempt to spread non-iterable instance."
when you pass a function to component, try to pass it's reference, instead of
<TodoItem
todoItem={item}
pressToToggle={() => this.props.toggleTodo(item)}
deleteTodo={() => this.props.removeTodo(item)}
/>
try
<TodoItem
todoItem={item}
pressToToggle={this.props.toggleTodo.bind(this)}
deleteTodo={this.props.removeTodo.bind(this)}
/>
and in your TodoItem component call the function like
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity
style={styles.todoItem}
onPress={() => this.props.pressToToggle(todoItem)} /* this line */
>
<Text
style={{
color: todoItem.completed ? '#aaaaaa' : '#f5f5f5',
textDecorationLine: todoItem.completed ? 'line-through' : 'none',
fontSize: 16 }}
>
{todoItem.text}
</Text>
<Button
title='Remove'
color='#ff5330'
onPress={this.props.deleteTodo}
/>
</TouchableOpacity>
</View>
);
}
}

How to access child component values from the parent in react native?

I have a login screen with following structure:
import Logo from '../components/Logo'
import Form from '../components/Form';
export default class Login extends React. Component {
<View style={styles.container} >
<Logo/>
<Form type="login"/>
<View style={styles.signUpTextCont}>
...
</View>
</View>
and here is my Form component:
export default class Form extends React.Component {
constructor (props){
super(props)
this.state = {
username : '',
password : ''
}
}
handleChangeUsername = (text) => {
this.setState({ username: text })
}
handleChangePassword = (text) => {
this.setState({ password: text })
}
render() {
return (
<View style={styles.container} >
<TextInput
ref={(input) => { this.username = input }}
onChangeText = {this.handleChangeUsername}
value = {this.state.username}
/>
<TextInput
ref={(input) => { this.password = input }}
onChangeText = {this.handleChangePassword}
value = {this.state.password}
/>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>{this.props.type}</Text>
</TouchableOpacity>
</View>
);
}
}
now I would like to have a checkLogin() method in Login screen (parent).
How can I access username and password values to check them in the Login screen?
I will be grateful if someone could help.
Try using ref keyword for accessing the child values.
<View style={styles.container} >
<Logo/>
<Form type="login"
ref={'login'}/>
<View style={styles.signUpTextCont}>
...
</View>
</View>
To Acess Child Component Values in parent:
onClick = () =>{
//you can access properties on child component by following style:
let userName = this.refs['login'].state.username;
let password = this.refs['login'].state.password;
}
you can use callback to send username and password to parent like this sample code:
Form:
handleChangeUsername = (text) => {
this.setState({ username: text })
this.props.userChange(text)
}
handleChangePassword = (text) => {
this.setState({ password: text })
this.props.passChange(text)
}
login:
add two state named user and pass and:
setUser = (text) => {
this.setState({user:text})
}
setPass = (text) => {
this.setState({pass:text})
}
checkLogin = () => {
// check user and pass state...
}
<Form
type="login"
userChange = {(text) => { this.setUser(text) } }
passChange = {(text) => { this.setPass(text) } }
/>
and now, user and pass is in state in login and you can check it.
I hope this can help you

React-native: How to get navigationOptions with Custom TabBarComponent

I'm new to react native and i'm trying to build a custom tab bar but i'm facing a problem when trying to display icons tab bar.
Here what i achieve so far.
Here my Custom TabBar component:
class TabBar extends Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const isCapture = route.routeName === 'AddExpenses';
const focused = index === navigation.state.index;
const color = focused ? activeTintColor : inactiveTintColor;
if (isCapture === true) {
return (
<TouchableOpacity
key={route.key}
style={Styles.tab}
onPress={() => (navigation.navigate('AddExpensesModal'))}
>
<Ionicons
name={ioniconsByPlatform('add-circle')}
style={Styles.icon}
size={26}
/>
</TouchableOpacity>
);
}
return (
<TouchableWithoutFeedback
key={route.key}
style={Styles.tab}
onPress={() => (isCapture ? navigation.navigate('CaptureModal') : jumpToIndex(index))}
>
<View style={Styles.tab}>
<Text style={{ color }}>{route.routeName}</Text>
</View>
</TouchableWithoutFeedback>
);
}
render() {
const {
navigation,
} = this.props;
const {
routes,
} = navigation.state;
return (
<View style={Styles.tabBar}>
{routes && routes.map(this.renderItem)}
</View>
);
}
}
export default TabBar;
My Tab Navigator:
const MainTabNavigator = TabNavigator({
Summary: { screen: SummaryScreen },
AddExpenses: { screen: ExpensesScreen },
Expenses: { screen: ExpensesScreen },
}, {
tabBarComponent: TabBar,
});
export default MainTabNavigator;
And an example of a screen where i try to set my TabBarIcon:
const SummaryScreen = () => (
<View style={Styles.container}>
<Text>Summary</Text>
</View>
);
SummaryScreen.navigationOptions = {
title: 'Summary',
tabBarIcon: props => <TabBarIcon {...props} name="pulse" />,
};
export default SummaryScreen;
I want to be able to display my tab bar icons thanks to the navigationOptions property.
Do you have any idea how i can do this ?
If you feel TabNavigator is not powerful enough(which I think it's far from powerful), you could always customize a navigator view.
Here is my notes for customize a navigator view to replace TabNavigator:
export default class SectionTabView extends React.Component {
static propTypes = {
navigation: PropTypes.object
};
constructor(props) {
super(props);
}
render() {
const {router, navigation} = this.props;
const {routes, index} = navigation.state;
/**
* ActiveScreen is the current screen you see when you change you navigation state in tab bar
*/
const ActiveScreen = router.getComponentForState(navigation.state);
return (
<View style={Styles.section_container}>
<ActiveScreen
navigation={addNavigationHelpers({
...navigation,
state: routes[index],
})}
/>
<SectionTabBar navigation={navigation}/>
</View>
);
}
}
export default class SectionTabBar extends React.Component {
static propTypes = {
navigation: PropTypes.object
};
constructor(props) {
super(props);
}
getTabButtomGroupView() {
const {navigation} = this.props;
const {routes, index} = navigation.state;
let tabButtomGroupView = [];
routes.map((route) => {
let styles = [Styles.eventSection_tab];
const isClicked = routes[index].routeName === route.routeName;
if(isClicked){
styles.push(Styles.eventSection_tabClicked);
}
tabButtomGroupView.push(
<TouchableOpacity
onPress={() => {
/**
* when the routeName is equal to current routeName, we should stop navigate action
*/
if (routes[index].routeName === route.routeName) {
return;
}
navigation.navigate(route.routeName);
}}
style={styles}
key={route.routeName}>
<Text style={{color:'white'}}>{SectionRouteConfig[route.routeName].navigationOptions.title}</Text>
</TouchableOpacity>
)
});
return tabButtomGroupView;
}
render() {
return (
<View style={Styles.section_tabContainer}>
{this.getTabButtomGroupView()}
</View>
);
};
}
//SectionRouteConfig.js
export const sectionRouteConfig = {
XXX: {
screen: XXX, navigationOptions: {
title: XXX
}
},
XXX: {
screen: XXX, navigationOptions: {
title: XXX
}
}
};
export const SectionNavigator = createNavigator(TabRouter(sectionRouteConfig))(SectionTabView);
//Usage
render() {
const {dispatch, navigationState} = this.props;
return (
<SectionNavigator
navigation={
addNavigationHelpers({
dispatch: dispatch,
state: navigationState
})
}
/>
)
}
by the way I also use redux.
If those codes are too much for you , you can check the official example here:https://github.com/react-community/react-navigation/blob/master/examples/NavigationPlayground/js/CustomTabs.js

Cards of Native Base dynamically in react native and Firebase

I have data to extract from Firebase and i did. It's displayed perfectly fine in a listview. But now i have to display them in cards from the native base . this is the code i tried https://github.com/GeekyAnts/NativeBase/issues/347 but i get an error : undefined is not an object
import CardItem from'./CardItem'
import {Container, Content, Card, Body, Title} from 'native-base';
export default class SixteenTab extends Component {
constructor(props) {
super(props);
this.itemsRef = this.getRef().child('docs/topics/topics_2016/');
}
componentDidMount() {
// start listening for firebase updates
this.listenForItems(this.itemsRef);
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
items: items
});
});
}
render() {
return (
<View>
<Card dataArray={this.state.items}
renderRow={(item) => this._renderItem(item)}>
</Card>
</View>
)
}
_renderItem(item) {
return (
<CardItem item={item}/>
);
}
}
CardItem Page
class CardItem extends Component {
render() {
return (
<View style={styles.listItem}>
<Text style={styles.liText}>{this.props.item.title}</Text>
</View>
);
}
}
That's the code i used but i keep getting an error like the image below --> any idea please
PS: all the items are been extracted from firebase database correctly, since i can see them in the console
After putting this line this.state = { items: [] }; in the constructor, i get this warning
when trying the second method of Irfan , i get this warning and nothing is displayed in the screen
that's the final i wrote and still not working
export default class SixteenTab extends Component {
constructor(props) {
super(props);
this.itemsRef = this.getRef().child('docs/topics/topics_2016/');
this.state = { items: null };
}
componentDidMount() {
this.listenForItems(this.itemsRef);
}
componentWillMount() {
this.setState({
items:[]
});
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
items: items
});
});
}
render() {
return (
<View>
<Content>
<Card>
{
this.state.items.map((item,index)=>{
return (
<CardItem key={index}>
<View>
<Text>{item.title}</Text>
</View>
</CardItem>
)
})
}
</Card>
</Content>
</View>
)
}
_renderItem(item) {
return (
<ListItem item={item}/>
);
}
As per native base doc, dataArray and renderRow is not support Card Component. So should update your render function.
render() {
return (
<Container>
<Content>
<Card>
{
this.state.items.map((item, index)=>{
return (
<CardItem key={index}>
<View>
<Text>{item.title}</Text>
</View>
</CardItem>
)
})
}
</Card>
</Content>
</Container>
)
}
If you want your cards SEPARATELY , you need to put the key attribute in Card, not in CardItem! Like this:
render() {
return (
<Container>
<Content>
{
this.state.items.map((item, index)=>{
return (
<Card key={index}>
<CardItem>
<Text>{item.title}</Text>
</CardItem>
<Card/>
)
})
}
</Content>
</Container>
)
}