I'm currently working on a React Native project for Android. Now I created a TabNavigator using React Navigations createMaterialTopTabNavigator, which displays two tabs:
It would be nice to use this TabNavigator as a reusable component. Therefore, I'm trying to pass props from the component that is calling the navigator. Unfortunately, I can't figure out how to pass the props correctly.
This is the component that calls the TabNavigator (named TwoTabsHorizontal here):
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {withNavigation} from 'react-navigation';
import Background from '../../atoms/Background/Background';
import TwoTabsHorizontal from '../../molecules/TwoTabsHorizontal/TwoTabsHorizontal';
class Main extends React.Component {
render() {
return (
<View style={styles.view}>
<Background background={true} title="Find Dogs" />
<TwoTabsHorizontal
headingLeftTab="criteria"
headingRightTab="names"
/>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
backgroundColor: '#151414',
height: '100%',
},
});
export default withNavigation(Main);
And this is the TabNavigator TwoTabsHorizontal:
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {createMaterialTopTabNavigator} from 'react-navigation-tabs';
import {createAppContainer} from 'react-navigation';
import TestScreen1 from '../../../screens/TestScreen1';
import TestScreen2 from '../../../screens/TestScreen2';
const TabNavigator = ({headingLeftTab}) =>
createMaterialTopTabNavigator(
{
One: {
screen: TestScreen1,
navigationOptions: {
tabBarLabel: {headingLeftTab},
},
},
Two: {
screen: TestScreen2,
navigationOptions: {
tabBarLabel: 'Names',
},
},
},
{
},
);
export const TwoTabsHorizontal = createAppContainer(TabNavigator);
As you can see, I try to pass the prop headingLeftTab from Main down to TwoTabsHorizontal to use it as label in navigationOptions. It gives the following error:
I already tried the approach that is suggested here.
Related
I am using redux to control an Ant Design Modal component with a boolean state. Basically it has a button that dispatch action to change the state, and the component will read the state value.
The state is changed properly but the component props value is not updating accordingly. Not sure why it is not working.
I have tried different approaches in reducer like creating a new boolean object to avoid mutating the state but no luck.
myAction.js
export const modalVisibilityOn = () => ({
type: 'MODAL_ON'
})
export const modalVisibilityOff = () => ({
type: 'MODAL_OFF'
})
myReducer.js
const modalVisibility = (state = false, action) => {
switch (action.type){
case 'MODAL_ON':
return true
case 'MODAL_OFF':
return false
default:
return state
}
}
export default modalVisibility
myRootReducer.js
import { combineReducers } from 'redux'
import modalVisibility from './signPage/myReducer'
export default combineReducers({
modalVisibility
})
myModal.js
import React from "react";
import PropTypes from 'prop-types'
import { Modal, Input } from 'antd';
import { connect } from 'react-redux'
import { modalVisibilityOff } from '../../reducers/signPage/myAction'
class myModal extends React.Component {
render() {
const { visibility, handleOk, handleCancel } = this.props;
myModal.propTypes = {
visibility: PropTypes.bool.isRequired,
handleOk: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired,
}
return (
<Modal
title="Sign"
visible={visibility}
onOk={handleOk}
onCancel={handleCancel}
closable={false}
>
<p>Please input your staff ID</p>
<Input addonBefore="Staff ID" />
</Modal>
);
}
}
const mapStateToProps = state => {
return {
visibility: state.modalVisibility
}
}
const mapDispatchToProps = dispatch => ({
handleOk: () => dispatch(modalVisibilityOff()),
handleCancel: () => dispatch(modalVisibilityOff()),
})
export default connect(
mapStateToProps,mapDispatchToProps
)(myModal)
myModalContainer.js
import React from "react";
import { Input } from "antd";
import { Button } from 'antd';
import { Row, Col } from 'antd';
import { Typography } from 'antd';
import PropTypes from 'prop-types'
import myModal from '../../dialogs/signPage/myModal';
import { connect } from 'react-redux'
import { modalVisibilityOn } from '../../reducers/signPage/myAction'
class myModalContainer extends React.Component {
render() {
const { Title } = Typography;
const { onClick } = this.props;
myModalContainer.propTypes = {
onClick: PropTypes.func.isRequired
}
return (
<div className="search-container-parent">
<Row className="search-container">
<Col className="search-col1" xs={24} sm={12}>
<Input size="large" style={{width:'40%'}} id="issueReturnNo" placeholder="QR code here"/>
<Button size="large">SEARCH</Button>
<div className="signBtn-div">
<Button size="large" type="primary" onClick={onClick} >SIGN</Button>
<myModal />
</div>
</Col>
<Col xs={24} sm={12}>
<Title className="issueLog-title" level={3} style={{color:"#F08080"}}>Issue</Title>
</Col>
</Row>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
onClick: () => dispatch(modalVisibilityOn())
})
export default connect(
null, mapDispatchToProps
)(myModalContainer);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import rootReducer from './myRootReducer'
const store = createStore(rootReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
serviceWorker.unregister();
I expect the visibility props on myModal.js would be true when the sign button on myModalContainer.js is clicked, but the it keep showing false.
Any help would be appreciated. Thanks!
After lots of researches and trials, It turns out that my code has no problem..
The reason why it is not working as expected, is due to the redux and react-redux version. After switching package.json dependencies versions back to the one that redux official tutorial are using, the application is running without any problem.
In case anyone have the same problem, here is the version I am using now for my app in npm:
redux: ^3.5.2
react-redux: ^5.0.7
Update:
Just found out that the cause of the problem comes from conflicts between older version modules and newer version modules.
Therefore by updating all the dependencies in package.json to the latest version, the app can also run smoothly. It is not necessary to downgrade react-redux and redux.
There is no errors and warnings in compiling but when i launch my project i get this error :
TypeError: translate is not a function
289 | inputProps={{
> 291 | placeholder: translate('labels.search') + '...',
I'm using react-admin 2.3.3, i can post my packages.json if you want.
I tried to clean my modules and install it again without success.
This is my compoment (simplified code) :
import React from 'react'
import PropTypes from 'prop-types'
import deburr from 'lodash/deburr'
import Autosuggest from 'react-autosuggest'
import match from 'autosuggest-highlight/match'
import parse from 'autosuggest-highlight/parse'
import TextField from '#material-ui/core/TextField'
import Paper from '#material-ui/core/Paper'
import MenuItem from '#material-ui/core/MenuItem'
import Popper from '#material-ui/core/Popper'
import ListItem from '#material-ui/core/ListItem'
import ListItemIcon from '#material-ui/core/ListItemIcon'
import ListItemText from '#material-ui/core/ListItemText'
import Avatar from '#material-ui/core/Avatar'
import Divider from '#material-ui/core/Divider'
import SearchIcon from '#material-ui/icons/Search'
import { withStyles } from '#material-ui/core/styles'
import { fade } from '#material-ui/core/styles/colorManipulator'
import { Link } from 'react-router-dom'
const styles = theme => ({})
class TestComponent extends React.Component {
render() {
const { classes } = this.props
const { translate } = this.context
return (
<div className={classes.root}>
<Autosuggest
inputProps={{
placeholder: translate('labels.search') + '...',
}}
/>
</div>
)
}
}
TestComponent.propTypes = {
classes: PropTypes.object.isRequired,
}
TestComponent.contextTypes = {
translate: PropTypes.func,
}
export default withStyles(styles)(TestComponent)
You must import the translate HOC from react-admin and apply it to your component:
import { translate } from 'react-admin`;
export default translate(withStyles(styles)(TestComponent));
Then, you should get the translate function injected by this HOC from your props instead of context:
const { classes, translate } = this.props
I'm creating an react native app using create-react-native-app, react-navigation and react-redux. I'm trying to add a redux-connected component as a screen into a nested StackNavigator (though the nesting seems to not make a difference, it doesn't work either way) and consistently am getting an error message saying Route 'MilkStash' should declare a screen. When I remove the redux connection from the MilkStash.js file, everything works fine. Any idea how to get this working?
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './src/reducers';
import AppWithNavigation from './src/AppWithNavigation';
export default () => (
<Provider store = {createStore(rootReducer)}>
<AppWithNavigation />
</Provider>
);
AppWithNavigation.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Image, Button } from 'react-native';
import { DrawerNavigator, StackNavigator } from 'react-navigation';
import MilkStash from './screens'
import { StatsScreen, FAQScreen, AddMilk, AccountScreen } from './screens';
export default class AppWithNavigation extends React.Component {
render() {
return (
<MenuNavigator />
);
}
}
const MilkNavigator = StackNavigator(
{ Milk: { screen: MilkStash},
AddMilk: { screen: AddMilk }
},
);
const AccountNavigator = StackNavigator(
{ Account: {screen: AccountScreen}}
);
const StatsNavigator = StackNavigator(
{ Stats: {screen: StatsScreen }}
);
const FAQNavigator = StackNavigator(
{ FAQ: {screen: FAQScreen}}
)
const MenuNavigator = DrawerNavigator({
Milk: { screen: MilkNavigator},
Account: {screen: AccountNavigator},
Stats: {screen: StatsNavigator},
FAQ: {screen: FAQNavigator},
}
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
}
});
MilkStash.js
import React, {Component} from 'react';
import { StyleSheet, Text, View} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { connect } from 'react-redux';
import { Milk } from '../../core/models/milk';
import styles from './styles.js';
export class MilkStash extends Component {
constructor(props){
super(props);
}
render() {
return (
<View style={styles.container}>
....displaying data goes here
</View>
)
}
}
function mapStateToProps(state){
return{
milkStash: state.milkStash
}
}
function mapDispatchToProps(dispatch){
return {
addMilk: (milk) => dispatch(addMilk(milk)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MilkStash);
milk-reducer.js
import {ADD_MILK} from '../constants';
const milkReducer = (state = {milkStash: []}, action = {}) => {
switch(action.type){
case ADD_MILK:
var item = action.payload;
return state
.update('milkStash', (milkStash) =>
{
var milkStashCopy = JSON.parse(JSON.stringify(milkStash));
milkStashCopy.concat(item);
return milkStashCopy;
});
default:
return state;
}
}
export default milkReducer;
reducers.js
export * from './milk.js';
import milkReducer from './milk';
import { combineReducers } from 'redux';
export default rootReducer = combineReducers({
milk: milkReducer
});
I figured out the answer and thought I would help prevent someone else struggling with this for 3 days. The issue had to do with the way I was importing the exports from MilkStash.js. Apparently using import MilkStash from './screens' will cause the error but changing it to import MilkStashContainer from './screens/MilkStash/MilkStash.js will fix the problem.
I'm new in React Native and trying create my first app. So I have a question:
I got 2 screens (using react-navigation). At first screen there is a render of app logo with spinner(from native-base) and fetch to the server at the same time. And I need to navigate to another screen only when fetch is over and responce is handled. Please help me find my mistakes!
index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,TouchableHighlight
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import LoadingScreen from './src/screens/LoadingScreen.js';
import MainContainer from './src/screens/MainContainer.js';
export default class Calculator2 extends Component {
render() {
return (
<LoadingScreen/>
);
}
}
const AppNavigator = StackNavigator({
Loading: {
screen: LoadingScreen
},
Main: {
screen: MainContainer
}
});
AppRegistry.registerComponent('Calculator2', () => Calculator2);
LoadingScreen.js:
import React, { Component } from 'react';
import {
AsyncStorage,
AppRegistry,NetInfo,
Text,Image,View
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import AppNavigator from '../../index.ios.js';
import { Container, Header, Content, Spinner } from 'native-base';
export default class LoadingScreen extends Component {
static navigationOptions = {
title: 'Loading',
};
constructor(props){
super(props);
}
componentDidMount(){
const {navigate} = this.props.navigation;
fetch('url').then( (response) => {navigate('Main')});
}
render() {
return(
<View>
App logo with spinner
</View>
);
}
}
MainContainer.js
import React, { Component } from 'react';
import {
AppRegistry,Alert,NetInfo,
StyleSheet,
Text,
View,ActivityIndicator,
TextInput,TouchableHighlight
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import AppNavigator from '../../index.ios.js';
export default class MainContainer extends Component {
static navigationOptions = {
title: 'Main',
};
render() {
return (
<View style={{flexDirection: 'column'}}>
...
</View>
);
}
}
And all I got is an error "Cannot read property 'navigate' of undefined" at LoadingScreen.componentDidMount
UPD
actually my fetch should be a function getting responce and handling it, and it should wait till handling is done:
async function getData(){
var response = await fetch('url', {
method: 'GET'
});
storage = await response.json(); // storage for response
regions = Object.keys(storage); // an array of regions names
console.log(storage, Object.keys(storage));
};
You need to register AppNavigator component instead of Calculator2
AppRegistry.registerComponent('Calculator2', () => AppNavigator);
Just update your LoadingScreen.js's componentDidMount function as following:
componentDidMount() {
var self = this;
fetch('url').then( (response) => {
self.props.navigation.navigate('Main')
});
}
I am trying to open a menu by dispatching an action, using react-navigation:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Image, TouchableHighlight, View } from 'react-native';
import { navigate } from './actions.js'
class HeaderLeft extends Component {
constructor(props) {
super(props);
this.showMenu = this.showMenu.bind(this);
}
showMenu() {
this.props.dispatch(navigate('DRAWER_OPEN'));
}
render() {
return (
<View>
<TouchableHighlight onPress={this.showMenu()}>
<Image source={require('../images/home-icon.png')} style={{width: 30, height: 30}} />
</TouchableHighlight>
</View>
);
}
}
export default connect()(HeaderLeft);
I am using redux, I get the error cannot read property 'navigate of undefined
I am importing my actions file below:
import { NavigationActions } from 'react-navigation';
export const navigate = (routeName, params, action) =>
NavigationActions.navigate({ routeName, params, action });
actions is undefined since you do not export anything by default in your file action.js. The correct import should be
import { navigate } from './actions.js';