Component in title of react-navigation navigationOptions, can't access redux properties - reactjs

I have a <TextElement> component that displays different static text based on the language that is set. The multi language text JSON and current language is set in the Redux store.
I would like this <TextElement> component to work in the header for page title, ie in navigationOptions. However this.props.texts or this.props.language are undefined which is what would normally be used in the component. I tried the solutions on here to no avail.
class PreviousBookingsList extends Component {
static navigationOptions = function (props) {
return {
title: <TextElement textId={2} texts={this.props.texts} language={this.props.language} />,
headerRight: <Icon name="menu" color='#fff' iconStyle={styles.menuIcon} underlayColor='transparent'
size={30} onPress={() => props.navigation.openDrawer()}/>
}
};
render() {
const data = this.props.data;
const villaList = data.PreviousBookings.map(function(villa) {
return (
<Villa key={villa.VillaName} {...villa} />
);
});
return (
<View style={styles.mainBg}>
<ScrollView>
{villaList}
</ScrollView>
</View>
);
}
}
function mapStateToProps(state) {
return {
texts: state.textsReducer.texts,
data: state.dataReducer.data,
language: state.languageReducer.language
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(PreviousBookingsList);
Any help appreciated.

Related

How to pass navigation props to react arrow function outside of default class that returns text with on press navigation

React Native Expo App:
In render of my default class I map from an array that calls an arrow function outside of the class to return a line of text for each object. I would like to pass the navigation prop through so that on press of that text will navigate to another screen.
My navigation is set up correct as it is called from a touchable opacity elsewhere in render to navigate to another screen.
I have stripped this back quite a bit but hopefully enough to explain:
const CustomDialogContent = ({name, ip, navigate}) => {
return (
<Text onPress={navigate('Webview')}>
{name} ({ip})
</Text>
)
}
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state ={
devices: [],
name: '',
ip: '',
};
}
static navigationOptions = {
title: 'App',
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={container}>
<Dialog
<DialogContent>
{this.state.devices.map((device) => {
return (
<CustomDialogContent name={device.name} ip={device.ip} navigation={navigate}/>
);}
</DialogContent>
</Dialog>
</View>
);
}
}
Instead of taking me to the screen I get navigate is undefined. I have also tried this which threw undefined is not an object:
const CustomDialogContent = ({name, ip}) => {
const {navigate} = this.props.navigation;
return (
<Text onPress={navigate('Webview')}>
{name} ({ip})
</Text>
)
}
I then tried this which returned navigate is not a function:
const CustomDialogContent = ({name, ip, navigate}) => {
return (
<Text onPress={() => {navigate('Webview')}}>
{name} ({ip})
</Text>
)
}
Apologies if I am missing something quite basic I am a junior and completely new to react
Final solution with credit to #LonelyCpp:
const CustomDialogContent = props => {
return (
<Text onPress={() => {props.navigation('Webview');}}>
{props.name} ({props.ip})
</Text>
);
};
<CustomDialogContent name={device.name} ip={device.ip} navigation={navigate}/>
props are like variable assignments. You've done navigation={navigate} which means in CustomDialogContent gets the function as props.navigation
this should work -
const CustomDialogContent = (props) => {
const navigate = props.navigation; // no {}
return (
<Text onPress={()=>navigate('Webview')}>
{name} ({ip})
</Text>
)
}
edit : removed this since its a functional component

how to properly export component containing multiple classes in react

I'm actually working on a small react app, I actually want to connect my component to firebase, but this component contains multiple classes and multiple exports, so when i apply my method (which is based on one class component) it rendering me nothing, it supposed to returns data from firestore.
when i try to console log the state on mapStateToProps it returns undefined :
const mapStateToProps = (state) => {
console.log("state firebase",state);
return {
animationsfb: state.firestore.ordered.animations,
}
}
that's my component that contains multiple classes:
export class AnimationScreen extends Component {
render() {
return (
<View>
.........
</View>
);
}
}
const mapStateToProps = (state) => {
console.log("state firebase",state);
return {
animationsfb: state.firestore.ordered.animations,
}
}
class DetailsScreen extends React.Component {
render() {
return (
<View>
.........
</View>
);
}
}
const Navigator = FluidNavigator({
home: {screen: AnimationScreen},
homeDetails: {screen: DetailsScreen},
},
);
class HomeTransitions extends React.Component {
static router = Navigator.router;
render() {
const {navigation} = this.props;
return (
<Navigator navigation={navigation}/>
);
}
}
// it was like this before i change it: **export default HomeTransitions**
export default compose(
connect(mapStateToProps), firestoreConnect([{ collection: 'animations'}])
) (HomeTransitions);
I expect to return me data on state when i console log it, but it returns undefined.
Currently you are trying to connect everything to the store, including the navigator, which is probably not what you want to do.
If you are just using animationsfb in AnimationScreen, just connect this component to the store and use the output as a screen in your navigator:
class AnimationScreen extends Component {
render() {
return (
<View>
// [...]
</View>
);
}
}
const mapStateToProps = (state) => {
console.log("state firebase", state);
return {
animationsfb: state.firestore.ordered.animations,
}
}
const AnimationScreenConnected = connect(mapStateToProps)(AnimationScreen);
Then in your navigator:
const Navigator = FluidNavigator({
home: { screen: AnimationScreenConnected },
homeDetails: { screen: DetailsScreen },
});

Context API in react native

Hey guys im just start learning about React native context api I want to know that how can I implement this as globally like global state
and its also not working after navigate to another screen and why do we include class name in provider <ProfileScreen screen= {this.state.contextData}/> can we do it globally..
here's my code
global.cart=1
const Context = React.createContext(global.cart)
class HomeScreen extends Component<Props> {
constructor(props){
super(props);
this.state={
contextData:5
}
}
Incrementhome=()=>{
this.setState({contextData:global.cart})
global.cart++
}
Decrementhome=()=>{
this.setState({contextData:global.cart})
global.cart--
}
render() {
return (
<View>
<Context.Provider value={this.state.contextData}>
<Button title="Incrementhome"
onPress={this.Incrementhome}/>
<Button title="decrementhome"
onPress={this.Decrementhome}/>
<ProfileScreen screen= {this.state.contextData}/>
</Context.Provider>
<Button title='sd' onPress={()=>{this.props.navigation.navigate('Profile')}}/>
</View>
)
}
}
class profile screen which can show my data
class ProfileScreen extends Component<Props> {
render() {
return (
<View style={{}}>
<Context.Consumer>
{data=> <Text style={{fontSize:50}}>{data}</Text>}
</Context.Consumer>
</View>
);
}
}
class profile screens that is also a provider
class ProfileScreens extends Component<Props> {
static navigationOptions =
{
title: 'MainActivity', header: <Button title='sd' onPress={()=>{this.props.navigation.navigate('ProfileScreen')}}/>
};
constructor(props){
super(props);
this.state={contextData:0
}
}
render() {
return (
<View >
<Context.Provider value={this.state.contextData}>
<Button title="decrement" onPress={()=>{ this.props.changeHomeScreen() }}/>
<Button title='sd' onPress={()=>{this.props.navigation.navigate(Profile)}}/>
</Context.Provider>
</View>
);
}
}
my navigator
export default HomeScreen = createStackNavigator({
HomeScreen:{
screen:HomeScreen
},
Profile:{
screen:ProfileScreen
},
ProfileScreens:{
screen:ProfileScreens
},
})
Sorry but you didn’t implement well the React Context API. Read this https://medium.com/#mcssym/react-context-api-why-you-dont-surely-need-redux-co-e6d96ca8abca?source=linkShare-1d75ea07b723-1539164899
The way you pass contextData via screen prop is useless if you use Context.Consumer.
The navigation.navigate take a string not a React Component as parameter.
I Don't really know how to explain you easily so i'll rewrite your code with how you must do that job.
YOUR NAVIGATOR (somewhere/navigation.js)
export default Home = createStackNavigator({
HomeScreen:{
screen: HomeScreen
},
Profile:{
screen: ProfileScreen
},
ProfileScreens:{
screen: ProfileScreens // Don't need to be a Provider
},
})
Your ProfileScreens Don't need to be a Provider because you don't use it as a wrapper. But can be a Consumer because you use the contextData. I guess it's the same as in Your HomeScreen and the one you want to make global.
//IMPORTANT
import { withHomeContext } from './somewhere/contexts/home';
class ProfileScreens extends Component<Props> {
static navigationOptions = {
title: 'MainActivity',
header: <Button title='sd' onPress={()=> this.props.navigation.navigate('ProfileScreen')}/>
};
constructor(props){
super(props);
this.state = {
contextData: props.homeProvider.contextData // Get from global context home provider
};
}
decrementHome = () => {
// Calling decrement from homeProvider
if(this.props.homeProvider) this.props.homeProvider.decrement();
}
render() {
return (
<View >
{/*You must call the decrementHome from your provider*/}
<Button title="decrement" onPress={this.decrementHome}/>
<Button title='sd' onPress={()=> this.props.navigation.navigate('ProfileScreen') }/>
</View>
);
}
}
export default withHomeContext(ProfileScreens);
YOUR ProfileScreen.You must change the way you create it as a Consumer. A better to use a function withHomeContext created in your HomeContext class.
//IMPORTANT
import { withHomeContext } from './somewhere/contexts/home';
class ProfileScreen extends Component<Props> {
render() {
return (
<View style={{}}>
<Text style={{fontSize:50}}>{this.props.homeProvider.contextData}</Text>
</View>
);
}
}
export default withHomeContext(ProfileScreen);
And finally your HomeContext with your Provider and Consumer could be:
// In Your context/home.js
const HomeContext = React.createContext();
export class HomeProvider extends React.Component {
state = {
contextData: 5 //Default Value
};
decrementHome = () => {
this.setState(prevState => {
contextData: prevState.contextData - 1;
});
}
incrementHome = () => {
this.setState(prevState => {
contextData: prevState.contextData + 1;
});
}
getValues = () => {
return {
contextData: this.state.contextData,
decrement: this.decrementHome, // Call via homeProvider prop
increment: this.incrementHome // Call via homeProvider prop
}
}
render() {
return (
<HomeContext.Provider value={this.getValues()}>
{this.props.children}
</HomeContext.Provider>
);
}
}
export function withHomeContext(Component) {
class ComponentWithContext extends React.Component {
render {
return (
<HomeContext.Consumer>
{(value) => <Component {...this.props} homeProvider={value} />
</HomeContext.Consumer>
);
};
}
return ComponentWithContext;
}
In Your root App now
import { HomeProvider } from './somwhere/context/home';
import Home from './somwhere/navigation';
export default class App extends React.Component {
render() {
return (
<HomeProvider>
<Home />
</HomeProvider>
);
}
}

React Native - Adding items to FlatList using Redux

I'm trying to implement a simple ToDo list exercise app with Redux.
I have a TextInput, a Button and a FlatList, the items are stored as an array in the store and handled by a reducer.
When the button is pressed an item with the text from the input should be
added to the list.
Currently when I press the button nothing happens, the list does not seem to render and new elements are not added.
I suspect something could be wrong in the onPress handling but I'm not sure, I could have made mistakes elsewhere, I'm pretty new to React Native
Any help for figuring this out is welcome.
This is my code so far (without the imports):
action
export const addItem = item =>({type: ADD_ITEM, payload: item});
reducer
const initialState = {
items: [],
};
const itemReducer = (state = initialState, action) => {
switch(action.type) {
case ADD_ITEM:
return { ...state, items: [...state.items, action.payload] };
default:
return state;
}
};
export default itemReducer;
store
const store = createStore(itemReducer);
export default store;
input form
const mapDispatchToProps = dispatch => {
return {
addItem: item => dispatch(addItem(item))
};
};
class ItemInput extends Component {
constructor(props) {
super(props);
this.state = { itemText: 'Add an item' };
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit() {
//event.preventDefault();
const { itemText } = this.state;
this.props.addItem( { itemText } );
this.setState({ itemText: "" });
}
render() {
return (
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(itemText) => this.setState({itemText})}
value={this.state.itemText}
/>
<Button
onPress={this.handleSubmit}
title="Submit"
color="#841584"
/>
</View>
);
}
}
export default ItemInput = connect(null, mapDispatchToProps)(ItemInput);
list
const mapStateToProps = state => {
return { items: state.items };
};
class ItemList extends Component {
constructor(props) {
super(props);
this.state = {
};
this.renderItem = this.renderItem.bind(this);
this.keyExtractor = this.keyExtractor.bind(this);
}
render() {
return (
<View style={{flex:1, backgroundColor: '#F5F5F5', paddingTop:20}}>
<FlatList
ref='listRef'
data={this.props.items}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}/>
</View>
);
}
keyExtractor = (item, index) => index.toString();
renderItem({item, index}) {
return (
<View style={{backgroundColor: 'white'}}>
<Text>{item.title}</Text>
</View>
)
}
}
export default List = connect(mapStateToProps)(ItemList);
todo list component
class ToDoList extends Component {
render() {
return (
<View>
<ItemInput/>
<List/>
</View>
);
}
}
export default ToDoList;
App (root)
export default class App extends Component {
render() {
return (
<Provider store={store}>
<ToDoList/>
</Provider>
);
}
}

React Native / React navigation, same level component

I have 2 classes: my default class HomeScreen used for the home page and another class MyList which I use to generate a flatlist on my HomeScreen.
My problem is that I do not succeed in building my navigation function in my MyList class.
I always get the following error: "Can't find variable: navigate".
I took a look at this Calling Navigate on Top Level Component but I really don't know how to implement it in my code.
Here's what I've tried:
class MyList extends React.Component {
_keyExtractor = (item, index) => item.note.id;
_renderItem = ({ item }) => (
<TouchableNativeFeedback
onPress={() => navigate('Note', { noteId: item.note.id })} >
<View>
<Text style={styles.noteElementTitle} >{item.note.title}</Text>
<Text style={styles.noteElementBody} >{item.note.body}</Text>
</View>
</TouchableNativeFeedback>
);
render() {
return (
<FlatList
data={this.props.data}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Notes',
headerStyle: { backgroundColor: 'rgb(255, 187, 0)' },
headerTitleStyle: { color: 'white' },
};
render() {
const { navigate } = this.props.navigation;
return (
<MyList
data={this.state.data}
load={this.state.load}
navig={this.props.navigation}
>
</MyList>
);
}
}
const Project = StackNavigator({
Home: { screen: HomeScreen },
NewNote: { screen: NewNoteScreen },
Note: { screen: NoteScreen }
});
AppRegistry.registerComponent('Project', () => Project);
Thanks for your help.
Because your MyList component is not part of your stack the navigation prop is not available for that.
You have 2 options.
First option you can pass the navigation prop manually to MyList
render() {
const { navigate } = this.props.navigation;
return (
<MyList
data={this.state.data}
load={this.state.load}
navigation={this.props.navigation}
>
</MyList>
);
}
Second option you can use withNavigation.
withNavigation is a Higher Order Component which passes the navigation
prop into a wrapped Component. It's useful when you cannot pass the
navigation prop into the component directly, or don't want to pass it
in case of a deeply nested child.
import { Button } 'react-native';
import { withNavigation } from 'react-navigation';
const MyComponent = ({ to, navigation }) => (
<Button title={`navigate to ${to}`} onPress={() => navigation.navigate(to)} />
);
const MyComponentWithNavigation = withNavigation(MyComponent);

Resources