how to properly export component containing multiple classes in react - reactjs

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 },
});

Related

Pass original component's function to react HOC

I have created react HOC component as below.
const UpdatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
componentDidMount(){
}
incrementCount = () => {
this.setState(prevState => {
return {counter:prevState.counter+1}
})
}
render(){
return <OriginalComponent
incrementCount={this.incrementCount}
count={this.state.counter}
/>
}
}
return NewComponent
}
export default UpdatedComponent
and I am using that component in the below example
class HoverCounter extends Component {
componentDidMount(){
}
handleMessages = () => {
// need to do somthing
}
render() {
const {incrementCount, count} = this.props
return (
<div onMouseOver={incrementCount}>
Hoverd {count} times
</div>
)
}
}
export default UpdatedComponent(HoverCounter)
I want to know that is it possible to pass
handleMessages()
function to HOC?
like this
export default UpdatedComponent(HoverCounter,handleMessages)
I have no idea how to pass the original component function or props to HOC.
you could get everyThing in your Hoc like this :
const UpdatedComponent = (OriginalComponent , func) => {
componentDidMount(){
func()
}
in HoverCounter also you could add this changes:
static handleMessages(){
// need to do something
}
export default UpdatedComponent(HoverCounter , HoverCounter.handleMessages)

passing textinput value from one screen into another using mapDispatchToProps

I want to pass value of a textinput from one screen to another using mapDispatchToProps. I am roughly a newbie in the redux world and I am a bit confused. kindly make corrections to my code below. I have tried using the example implemented on the documentation, however, I do not fully understand mapDispatchToProps.
PS I tried to keep the code as simple as possible for better understanding
Screen1
import React, { Component, Fragment } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
class Screen1 extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
total: 1,
};
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText(number) {
const total = parseInt(number);
if (number.length === 0) {
this.setState({ total: '' });
} else {
this.setState({ total });
}
}
render() {
return (
<SafeAreaView style={styles.AndroidSafeArea}>
<View style={styles.wrapper}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollableList}
>
<InputField
children={"Receiver's phone no."}
iconType={'ios-call'}
placeholder={"number"}
keyboardType={'phone-pad'}
maxLength={11}
/>
<InputField
children={"Receiver's gifts"}
iconType={'ios-basket'}
placeholder={'Gifts'}
keyboardType={'phone-pad'}
maxLength={2}
onChangeText={this.onChangeText}
value={this.state.total.toString()}
/>
</ScrollView>
</View>
</SafeAreaView>
);
}
}
function mapDispatchToProps(dispatch) {
return {
total: () => {
dispatch(this.onChangeText());
}
}
}
export default connect(mapDispatchToProps) (Screen1);
Screen2
import React, { Component, Fragment } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
class Screen2 extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<SafeAreaView style={styles.AndroidSafeArea}>
<View style={styles.wrapper}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollableList}
>
<Text>{this.props.onChangeText}</Text>
</ScrollView>
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return {
total: state.onChangeText
}
}
}
export default connect(mapStateToProps) (Screen2);
Reducer.js
import { TOTAL_GIFTS } from '../actions/types';
const initialState = {
total: ''
};
const Reducer = (state = initialState, action) => {
switch (action.type) {
case TOTAL_GIFTS:
return {
...state,
total: action.total
};
default:
return state;
}
};
export default Reducer;
Im leaving the none redux related parts of your code out.
Screen1
class Screen1 extends React.component{
handleChange(number){
this.props.announceChange(number)
}
}
//mapping dispatch to props
function mapDispatchToProps(dispatch){
announceChange(number)
}
//action creator
function announceChange(number){
return {type:'SOME_CONSTANT',payload:number}
}
export default connect(null,mapStateToProps)(Screen1)
Reducer:
export default function reducer(state={},action){
switch(action.type){
case 'SOME_CONSTANT':
return {...state,number:action.payload}
default :
return state;
}
}
screen2:
class Screen2 extends React.component{
render(){
const {number} = this.props
return(
<span>{number}</span>
)
}
}
function mapStateToProps(state){
return {
number : state.reducername
}
}
export default connect(mapStateToProps)(Screen2);
the above code is a minimal sample of the way you can use redux. if you dont have any ideas how to setup your store,reducer and other redux stuff reading this wont take more than 10 mints.
mapDispatchToProps: are functions/actions to update store(redux)
mapStateToProps : to get data from store(redux)
on first screen you will disptach action to update email using mapDispatchToProps
on second you will get email from mapStateToProps
I have created a sample code for you (CHECK IN ANDROI/IOS)
Please check https://snack.expo.io/#mehran.khan/reduxtest
APP PREVIEW

How do I get state or props into TabNavigator

I'd like to get user image into the tabBarIcon just like Instagram does it. I can't figure out the way to do it.
I tried getting the state but on the init of the app the state is empty.
I've tried like
const store = store.getState().user
but it's undefined on app init
I have MainTabNavigator.js
ProfileStack.navigationOptions = {
tabBarLabel: () => {
return null
},
tabBarIcon: ({focused}) => (
<Image source={{uri: ???}}/>
)
}
const TabNavigator = createBottomTabNavigator({
HomeStack,
SearchStack,
DashboardStack,
ProfileStack,
});
TabNavigator.path = '';
export default TabNavigator;
I can't get the props or state since this isn't a class
The solution was quite simple.... I don't know why I didn't think of it earlier.
The trick was to create a component that would get the state from redux and when the state changes after the init, the state would finally have the Object and there the image path
ProfileStack.navigationOptions = {
tabBarLabel: () => {
return null
},
tabBarIcon: ({focused}) => (
<BottomTabImage focused={focused} />
)
}
See i changed it to BottomTabImage which is just a component
And this is the component file
BottomTabImage.js
import {connect} from 'react-redux';
class BottomTabImage extends Component {
render() {
const uri = this.props.auth.image !== undefined ?`http://localhost/storage/creator_images/${this.props.auth.image)}`: '';
return <Image style={styles.profileImage} source={{uri}} />
}
}
const styles = StyleSheet.create({
profileImage: {
...
}
});
function mapStateToProps(state) {
return {
auth: state.user.auth
}
}
export default connect(mapStateToProps, {})(BottomTabImage)

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>
);
}
}

How do you navigate to another component that does not receive the props of react navigation?

I'm working with React Native and React Navigation.
I have a component called App.js in which I declare the Drawer Navigation of React-Navigation.
In this I have an option to log out but I can not navigate to another component after removing the AsyncStorage
Does anyone know how to achieve it?
Thank you.
This is my code:
App.js
import { createDrawerNavigator, DrawerItems, NavigationActions } from 'react-navigation';
const customDrawerComponent = (props) => (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<DrawerItems
{...props}
/>
<TouchableOpacity style={styles.button} onPress={this.logOut} >
<Text> Logout </Text>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
logOut = () => {
// NOT WORKS
// this.props.navigation.navigate('Login')
//NOT WORKS:
this.myAction();
}
myAction = () => {
const nav = NavigationActions.navigate({
routeName: 'App',
});
return nav;
};
const AppDrawNavigator = createDrawerNavigator(
{
MainComponent: { screen: MainComponent,
navigationOptions: ({navigation}) => ({
drawerLockMode: 'locked-closed'
}) },
Login: { screen: LoginComponent,
navigationOptions: ({navigation}) => ({
drawerLockMode: 'locked-closed'
}) },
User: { screen: UsersComponent }
},
{
contentComponent: customDrawerComponent,
}
);
make this as a class like
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
From your question I understand that either you want to :-
navigate from outside the components
navigate from components which do not have navigation prop.
For this I have tried 2 solutions and both work extremely fine though I based towards the second one.
First Solution
Use withNavigation from react-navigation package. If your components are deeply nested they wont have navigation prop unless u specify them manually or put them in context ;passing navigation prop becomes a real pain. So instead use withNavigation and your component would have navigation prop.
import {withNavigation} from "react-navigation";
const Component = ({navigation}) => {
const onPress = () => {
navigation.navigate(//ROUTE_NAME//)
}
return (
<TouchableOpacity onPress={onPress}>
<Text>Navigate</Text>
</TouchableOpacity>
)
}
export default withNavigation(Component);
Second Solution
Create a helper script and use that.
"use strict";
import React from "react";
import {NavigationActions} from "react-navigation";
let _container; // eslint-disable-line
export const navigation = {
mapProps: (SomeComponent) => {
return class extends React.Component {
static navigationOptions = SomeComponent.navigationOptions; // better use hoist-non-react-statics
render () {
const {navigation: {state: {params}}} = this.props;
return <SomeComponent {...params} {...this.props} />;
}
}
},
setContainer: (container) => {
_container = container;
},
reset: (routeName, params) => {
_container.dispatch(
NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName,
params
})
]
})
);
},
goBack: () => {
_container.dispatch(NavigationActions.back());
},
navigate: (routeName, params) => {
_container.dispatch(
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName,
params
})
);
},
navigateDeep: (actions) => {
_container.dispatch(
actions.reduceRight(
(prevAction, action) =>
NavigationActions.navigate({
type: "Navigation/NAVIGATE",
routeName: action.routeName,
params: action.params,
action: prevAction
}),
undefined
)
);
},
getCurrentRoute: () => {
if (!_container || !_container.state.nav) {
return null;
}
return _container.state.nav.routes[_container.state.nav.index] || null;
}
};
In your parent component when you mount the navigation call following:-
"use strict";
import React from "react";
import App from "./routes";
import {navigation} from "utils";
class Setup extends React.Component {
render () {
return (
<App
ref={navigatorRef => {
navigation.setContainer(navigatorRef);
}}
/>
);
}
}
export default App;
Now, in your components you can directly use helpers from this script itself and navigation would be accessibly globally now.
import {navigate} from "utils/navigation";
For more details you can this thread
Your logout function is declared outside of the Navigator. This means you don't have access to the navigation prop there. However, your customDrawerComponent is a screen of your Navigator and it should have access to it.
So you can try something like this (props here are the props passed to the customDrawerComponent):
onPress={()=> {props.navigation.navigate("Login")}}
Plus your App.js seems kind of strange since you're not exporting any component. Have you pasted the whole code of App.js or just parts of it?

Resources