Context API in react native - reactjs

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

Related

How do I transfer an variable defined in one file to another file?(React-Native)

How can I use the number variable defined in home.js in another file?
constructor(props) {
super(props);
this.getData();
this.state = {
number:'0',
};
}
As said here, you can do it like this:
class Home extends Component {
func(val) {
this.setState({value: val});
}
render() {
return (
<View>
<Two func={(val) => this.func(val)} />
</View>
)
}
}
class Two extends Component {
render() {
return (
<View>
<Button title="set" onPress={() => this.props.func('data')} />
</View>
)
}
}

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

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