useContext not unique to every component in flatlist? - reactjs

I have this flatlist which lists all the jobs,upon click we are taken to a Job details page where you can apply to the job. On apply, the button name changes to "applied" and it is disabled. However when I apply to any job, all jobs are disabled. How do I make sure the context is unique to every job in the flatlist?
Home.js:
export default function Home(params) {
const{ navigation}=params;
...
return(
<FlatList
data={getCompanies(companies, filter)}
renderItem={({ item }) => {
return(
<TouchableOpacity onPress={()=>{ navigation.navigate('Job Details',item)}}>
<View style={styles.listingCard}>
<View style={styles.listingCardBody}>
<Text style={styles.listingCardTitle}>{item.title}</Text>
<Text style={styles.listingCardDescription}>
Here is a really simple description from {item.title} about the
role being advertized...
</Text>
</View>
</View>
</TouchableOpacity>
);
}
/>
);
}
Jobdetails:
import {clickableContext} from '../clickableContext';
import {applyButtonContext} from '../applyButtonContext';
function Jobdetails({route, navigation}){
const{ title, Category, rateHourly, responsibilities,location, postedBy,id,jobOverview, jobType, startDate, endDate, latitude, longitude}= route.params;
const { butText, setButText } = useContext(applyButtonContext);
const { clickable, setClickable } = useContext(clickableContext);
return(
<TouchableOpacity disabled={ clickable } style={page.button} onPress={()=>{
//apply() works fine
apply(id).then(async (result)=>{
if(result.succeeded()){
setButText('Application Successful!');
setClickable(true);
}
});
}}>
<Text style={page.text}>{ butText }</Text>
</TouchableOpacity>
);
}
app.js:
import 'react-native-gesture-handler';
import React, {useContext, useState} from 'react';
...
import { applyButtonContext } from './src/api/jobs/applyButtonContext';
import { clickableContext} from "./src/api/jobs/clickableContext";
export default function App() {
const [clickable, setClickable] = useState(false);
const [ butText, setButText] = useState('Apply Now');
return(
<>
{/* Providers */}
<clickableContext.Provider value={{ clickable, setClickable }}>
<applyButtonContext.Provider value={{butText, setButText}}>
<SafeAreaProvider>
<Provider store={store}>
<ThemeProvider>
<NavigationContainer theme={loremipsum}>
<StatusBar barStyle="dark-content" />
<AppNavigation />
</NavigationContainer>
</ThemeProvider>
</Provider>
</SafeAreaProvider>
</applyButtonContext.Provider>
</clickableContext.Provider>
</>
);
}

Related

React-native navigation // move specific Drawer menu to title bar right button and hide the bottom menu which is linked with moved button

I'm using React-Navigation in React-native, and I'm a newbie to this, so I'm facing a number of difficult issues.
I am designing navigations with a mixture of Stack, Bottom and Drawer navigation.
My app has Intro Screen, Dashboard Screen, Profile Screen, Contact Screen.
Example Image
I thought almost successful, but I can't solve the problems below.
I want to move the "dashboard" menu(1) in Drawer to drawer title bar right button(2).
And I want to get rid of the duplicate "Dashoboard" menu(3) at the bottom.
my code..
// App.js
import React from 'react';
import Main from './components/Main';
class App extends React.Component {
render() {
return (
<Main />
);
}
}
// components/Main.js
import Intro from './__intro';
import DrawerNavigator from '../navigation/DrawerNavi';
const Stack = createStackNavigator();
class Main extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name='Intro'
component={Intro}
options={
{headerShown: false}
}
/>
<Stack.Screen
name='Index'
component={DrawerNavigator}
options={
{headerShown: false}
}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
}
export default Main;
// components/__intro.js
class Intro extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('Index')
}
}
>
<Text style={styles.text}>Go to Dashboard</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
// navigation/DrawerNavi.js
import TabNavigator from "./TabNavigator";
import IndexTap from "../components/Index";
import DashBord from "../components/DashBoard";
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="dashboard from drawerNavi"
component={IndexTap}
/>
<Drawer.Screen name="Contact" component={Contact} />
</Drawer.Navigator>
);
}
export default DrawerNavigator;
// components/Index.js
import DashBoard from './DashBoard';
import Profile from './Profile';
const Tap = createBottomTabNavigator();
class IndexTap extends Component {
render() {
return (
<Tap.Navigator>
<Tap.Screen
name="DashBoard"
component={DashBoard}
options={{
headerShown: false,
),
}}
// initialParams={{ title: 'DashBoard' }}
/>
<Tap.Screen
name="Profile"
component={Profile}
options={{
headerShown: false
// headerTitleAlign: 'center'
}}
// initialParams={{ title: 'Profile' }}
/>
</Tap.Navigator>
)
}
}
// components/Contact.js
const Contact = () => {
return (
<View style={styles.center}>
<Text>This is the contact screen</Text>
</View>
);
};
// components/Dashboard.js
class DashBoard extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<Text>DashBoard Page</Text>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('Intro')
}
}
>
<Text style={styles.text}>Go to the Intro</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
// components/Profile.js
class Profile extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.container_child_1}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.button}
onPress={
() => {
this.props.navigation.navigate('DashBoard')
}
}
>
<Text style={styles.text}>DashBoard</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
how can I do?
Do I need to redesign?

I am trying to use react context api with react native class components but getting an error undefined provider.default.consumer

I am creating a component called provider to store my data
import React from "react";
export const MyContext = React.createContext();
export class MyProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false
};
}
render() {
return (
<MyContext.Provider
value={{
state: this.state,
changeAuthState: value => this.setState({ isAuthenticated: true })
}}
>
{this.props.children}
</MyContext.Provider>
);
}
}
I am trying to consume that data from a nested component called add photo so when the user is done with the account setup it will change the data to authenticated = true and show the main stack
//addPhoto.js
import Provider from "../../../provider";
export default class AddPhoto extends React.Component {
constructor(props) {
super(props);
this.state = {
image: null
};
}
render() {
return (
<Provider.Consumer>
{
(context = () => (
<View style={styles.conatiner}>
<View style={styles.authIocn}>
<SimpleLineIcons name="camera" size={wp("30%")} />
</View>
<View style={styles.header}>
<Text style={styles.headerText}>Add Profile Photo</Text>
</View>
<View style={styles.paragraph}>
<Text style={styles.paragraphText}>
Add a profile photo so your friends know its you.
</Text>
</View>
<Button block style={styles.button} onPress={this._pickImage}>
<Text style={styles.buttonText}>Add a Photo</Text>
</Button>
<View style={styles.skipButtonView}>
<TouchableOpacity>
<Text style={styles.skipButtonText}>Skip</Text>
</TouchableOpacity>
</View>
</View>
))
}
</Provider.Consumer>
);
}
}
getting error typeError: undefined is not an object evaluating ('_provider.default.Consumer')
I had to export the Provider class and context from provider.js
import React from "react";
const MyContext = React.createContext();
class Provider extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: "false"
};
}
render() {
return (
<MyContext.Provider
value={{
state: this.state,
changeAuthState: value => this.setState({ isAuthenticated: value })
}}
>
{this.props.children}
</MyContext.Provider>
);
}
}
export { Provider, MyContext };
Then, i needed to wrap my app.js in the provider
import { Provider } from "./provider";
return (
<Provider>
<NavigationContainer>
{signedIn ? (
<TabStack.Navigator
barStyle={{
backgroundColor: "#15212B",
borderColor: "#B0B8B3",
borderTopWidth: hp("0.05")
}}
>
<TabStack.Screen name={"Feed"} component={Feed} />
<TabStack.Screen name={"Discover"} component={Discover} />
<TabStack.Screen name={"Account"} component={Account} />
</TabStack.Navigator>
) : (
<AuthStack.Navigator headerMode={false}>
<AuthStack.Screen name={"SignUp"} component={SignUp} />
<AuthStack.Screen
name={"EmailOrPhone"}
component={EmailOrPhone}
/>
<AuthStack.Screen name={"SignIn"} component={SignIn} />
</AuthStack.Navigator>
)}
</NavigationContainer>
</Provider>
);
}
}
Then finally wrap my addPhoto.js file in the context
import { MyContext } from "../../../provider";
return (
<MyContext.Consumer>
{context => (
<View style={styles.conatiner}>
<View style={styles.authIocn}>
<SimpleLineIcons name="camera" size={wp("30%")} />
</View>
<View style={styles.header}>
<Text style={styles.headerText}>Add Profile Photo</Text>
</View>
<View style={styles.paragraph}>
<Text style={styles.paragraphText}>
Add a profile photo so your friends know its you.
</Text>
</View>
<Button block style={styles.button} onPress={this._pickImage}>
<Text style={styles.buttonText}>Add a Photo</Text>
</Button>
<View style={styles.skipButtonView}>
<TouchableOpacity>
<Text style={styles.skipButtonText}>Skip</Text>
</TouchableOpacity>
</View>
</View>
)}
</MyContext.Consumer>
);
}}

How to replace component did update using react hooks in a functional component?

I have developed a react native application. In there I am updating a state variable through redux-thunk action dispatch. Now what I want is I want to navigate to another component when It is updated. How I have written is like this.
useEffect(() => {
_navigateOnSuccessOtp(props.navigation);
}, [props.success, props.navigation]);
This is navigated to next view even when navigation is changed because It also has been added as a dependency to this effect. This is my full code of the component.
import React, {useEffect} from 'react';
import {
View,
TouchableOpacity,
KeyboardAvoidingView,
Switch,
TextInput,
BackHandler,
} from 'react-native';
import I18n from 'react-native-i18n';
import {Formik} from 'formik';
import * as Yup from 'yup';
import {connect} from 'react-redux';
import {signinActions} from '_store/actions';
import SubmitButton from '_components/submitButton/';
import AppText from '_components/appText';
import {strings} from '_translations/i18n';
import styles from './loginstyle';
import Icon from 'react-native-vector-icons/FontAwesome';
const keepMe = (e) => {
console.log(e);
};
const handleBackButton = () => {
return true;
};
const _onPress = (values, sendOTP) => {
console.log(values.mobileNo);
sendOTP(values.mobileNo);
// navigation.navigate('Otp');
};
const _navigateOnSuccessOtp = (navigation) => {
navigation.navigate('Otp');
};
const changeLanguage = (navigate) => {
navigate.navigate('Home');
};
const getCurrentLocale = () => {
let locale;
locale = I18n.locale;
if (locale === 'en') {
return <AppText styles={styles.bottomLinkText}>English</AppText>;
} else if (locale === 'ta') {
return <AppText styles={styles.bottomLinkText}>தமிழ்</AppText>;
}
};
const Login = (props) => {
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBackButton);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBackButton);
};
}, []);
useEffect(() => {
_navigateOnSuccessOtp(props.navigation);
}, [props.success, props.navigation]);
return (
<KeyboardAvoidingView style={{flex: 1}} enabled>
<View style={styles.signinView}>
<View style={styles.signinTitleView}>
<AppText styles={styles.signinTitle}>
{strings('login.title')}
</AppText>
</View>
<View style={styles.colempty} />
<View style={styles.signIn}>
<View style={styles.signInContainer}>
<AppText styles={styles.loginFormTitle}>
{strings('login.title')}
</AppText>
<View style={styles.formContainer}>
<Formik
initialValues={{
mobileNo: '',
toggle: true,
}}
validationSchema={Yup.object({
mobileNo: Yup.string().required('Mobile number required'),
})}
onSubmit={(values, formikActions) => {
_onPress(values, props.sendOTP);
setTimeout(() => {
formikActions.setSubmitting(false);
}, 500);
}}>
{(formprops) => (
<View>
<View
style={
(!formprops.touched.mobileNo
? styles.inputView
: null) ||
(formprops.values.mobileNo && !formprops.errors.mobileNo
? styles.validInputView
: null) ||
(formprops.touched.mobileNo && formprops.errors.mobileNo
? styles.inputViewError
: null)
}>
<TextInput
style={styles.textField}
placeholder={strings('login.mobile')}
placeholderTextColor="#bbbbbb"
value={formprops.values.mobileNo}
onChangeText={formprops.handleChange('mobileNo')}
onBlur={formprops.handleBlur('mobileNo')}
keyboardType="numeric"
/>
{formprops.touched.mobileNo &&
formprops.errors.mobileNo ? (
<Icon name="times" size={20} style={styles.errorIcon} />
) : null}
{formprops.touched.mobileNo &&
formprops.values.mobileNo ? (
<Icon name="check" size={20} style={styles.validIcon} />
) : null}
</View>
{formprops.touched.mobileNo && formprops.errors.mobileNo ? (
<View style={styles.errorMessage}>
<AppText styles={styles.errorMessageText}>
{formprops.errors.mobileNo}
</AppText>
</View>
) : null}
<View style={styles.togglebuttoncontainer}>
<View style={styles.toggleTextView}>
<AppText styles={styles.toggleText}>
{strings('login.keep-login')}
</AppText>
</View>
<View style={styles.toggleView}>
<Switch
trackColor={{false: '#dddddd', true: '#c1d6ee'}}
thumbColor={{false: '#ffffff', true: '#007aff'}}
ios_backgroundColor="#dddddd"
onValueChange={(value) =>
formprops.setFieldValue('toggle', value)
}
value={formprops.values.toggle}
style={styles.toggle}
/>
</View>
</View>
<SubmitButton
onpress={formprops.handleSubmit}
btext={strings('login.button-text')}
/>
</View>
)}
</Formik>
</View>
</View>
</View>
<View style={styles.hr} />
<View style={styles.signInBottomContainer}>
<View style={styles.signInBottomView}>
<View style={styles.signInBottomContainerTextView}>
<AppText styles={styles.signInBottomContainerText}>
{strings('login.language-change')}?
</AppText>
</View>
<View style={styles.signInBottomLinkView}>
<TouchableOpacity
onPress={() => changeLanguage(props.navigation)}>
{getCurrentLocale()}
</TouchableOpacity>
</View>
</View>
</View>
</View>
</KeyboardAvoidingView>
);
};
const mapStateToProps = (state) => {
console.log(state);
return {
success: state.signin.success,
};
};
const mapDispatchToProps = (dispatch) => {
return {
sendOTP: (number) => dispatch(signinActions.sendOtpActionCreator(number)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);
I tried a lot to figure out how to do it correctly but I was unable to do so. So can someone help me with this problem? Thank you.
Since , _navigateOnSuccessOtp only uses navigation.navigate you can simply pass props.navigation.navigate as a dependency for useEffect and also as an argument to the function. Now navigate does not change and hence your useEffect won't be triggered unnecessarily.
const _navigateOnSuccessOtp = (navigate) => {
navigate('Otp');
};
.....
useEffect(() => {
_navigateOnSuccessOtp(props.navigation.navigate);
}, [props.success, props.navigation.navigate]);
You could also remove props.navigation from the dependency as you don't want useEffect to trigger on props.navigation change. Please check this post for more details: How to fix missing dependency warning when using useEffect React Hook?

datepickerIOS not coming on the screen

I have a row where date is getting displayed on click of that view, I want a datepicker to pop up and display.
class EditProfilePicture extends React.Component {
state = {
currentDate: new Date()
};
setCurrentDateForIOS = currentDate => {
this.setState({ currentDate });
};
pickDate = () => (
<View>
{Platform.OS === "ios" ? (
<DatePickerForIOS
setCurrentDateForIOS={this.setCurrentDateForIOS}
/>
) : (
DatePickerForAndroid().then(currentDate =>
this.props.changeBirthDate({ currentDate })
)
)}
</View>
);
render() {
return (
<View>
<View style={styles.Container}>
<Text style={styles.heading}>Your birthday</Text>
<TouchableWithoutFeedback onPress={this.pickDate}>
<View style={styles.dropdown}>
<Text style={styles.textStyle}>
{DateFormatter(this.state.currentDate)}
</Text>
<Icon name="chevron-down" color={MED_GREY} />
</View>
</TouchableWithoutFeedback>
</View>
</View>
);
}
}
And in DatePickerForIOS I have this---
import React from "react";
import PropTypes from "prop-types";
import { DatePickerIOS } from "react-native";
const DatePickerForIOS = props => {
return (
<DatePickerIOS
date={new Date()}
mode="date"
onDateChange={() => props.setCurrentDateForIOS}
/>
);
};
DatePickerIOS.propTypes = {
setCurrentDateForIOS: PropTypes.func.isRequired
};
export default DatePickerForIOS;
While the code works fine for android, in IOS the screen contains NAN/NAN/NAN as text initially and the datepicker doesn't open up when clicked on the view.
DatePickerIOS needs to be part of the render function of EditProfilePicture
Currently you create the component fromonPress from TouchableWithoutFeedback.
You would need to do something like the docs say
render() {
return (
<View style={styles.container}>
<DatePickerIOS
date={this.state.chosenDate}
onDateChange={this.setDate}
/>
</View>
)
}
You would need to hide/show this a state boolean value to toggle on off.
Without knowing your Android code, I suspect that is acting like a popup modal.
Another solution would be to use a package like react-native-modal-datetime-picker which has the same interface between iOS and Android

React-native props.navigation.navigate undefined

I really can't get my navigation to work. My navigation works in DrawerNavigator menu but it does not work in components. I've tried everything but there must be something I still do not understand.
I always get an error: "TypeError: undefined is not an object (evaluating'_this.props.navigation')"
This is my View:
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../../store/configure-store';
import StoresListContainer from '../../packages/stores/containers/list-container';
const store = configureStore({});
const Stores = () => {
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
};
export default Stores;
This is my Component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
ActivityIndicator,
FlatList,
Image,
TouchableNativeFeedback,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Styles from './styles';
import Theme from '../../../config/theme';
type Props = {
error: boolean,
loading: boolean,
data: Object,
fetchData: Function,
};
class ListComponent extends Component<Props> {
constructor(props) {
super(props);
this.goToPage = this.goToPage.bind(this);
}
componentDidMount() {
this.props.fetchData();
}
goToPage(param) {
this.props.navigation.navigate(param);
}
render() {
const hasData = Object.keys(this.props.data).length;
const errorMessage = () => {
return (
<Text>Ops! Ocorreu um erro ao carregar os dados.</Text>
);
};
const renderData = (item) => {
return (
<TouchableNativeFeedback contact={item} onPress={() => this.goToPage('Store')}>
<View style={Styles.renderData}>
<View style={{ flex: 0 }}>
<Image style={Styles.renderDataPicture} source={{ uri: item.item.image }} />
</View>
<View style={{ flex: 2 }}>
<Text style={Styles.renderDataTitle}>{ item.item.fantasy_name }</Text>
<Text style={Styles.renderDataSubtitle}>{ item.item.location }</Text>
</View>
<View style={Styles.renderDataItemIconContainer}>
<Icon name="chevron-right" style={Styles.renderDataItemIcon} />
</View>
</View>
</TouchableNativeFeedback>
);
};
const renderLoading = () => {
return (
<View style={Styles.renderLoading}>
<ActivityIndicator color={Theme.color.secondary} size="large" />
<Text style={Theme.text}>Aguarde</Text>
</View>
);
};
const getData = (data) => {
return (
<FlatList data={data} renderItem={renderData} keyExtractor={(item, index) => index} />
);
};
return (
<View style={Styles.container}>
{ this.props.loading ? renderLoading() : null }
{ this.props.error ? errorMessage() : null }
{ hasData ? getData(this.props.data) : null }
</View>
);
}
}
export default ListComponent;
You are using a functional stateless component instead of a class, therefore you need to drop the this.
const Stores = (props) => {
return (
<Provider store={store}>
<StoresListContainer navigation={props.navigation} />
</Provider>
);
};
// ES6 Class
class Stores extends Component {
render(){
return (
<Provider store={store}>
<StoresListContainer navigation={this.props.navigation} />
</Provider>
);
}
}

Resources