Centralized Error Handing with React-native and Redux - reactjs

I'm doing centralized the Error Handing in react-native. I'm able to do this just showing the error message whenever error is thrown.
I've created a ErrorNotification added to the very top of the component hierarchy of the app.
const App = () =>
<ErrorNotification>
<Login />
</ErrorNotification>
//ErrorNotification
const ErrorNotification = ({ children, errorCode, errorShow }) => {
return errorShow || errorCode ? <Error {...{errorCode}} /> : children;
};
function mapStateToProps(state) {
return {
errorCode: state.errorReducer.errorCode,
errorShow: state.errorReducer.errorShow
};
}
export default connect(mapStateToProps)(ErrorNotification);
//Error
const Error = ({errorCode }) => {
const errorMes = getMessageByCode(errorCode)
return (
<View style={styles.container}>
<Text>{errorMes}</Text>
<TouchableOpacity onPress={() => reuqestedByUser()}>
<Text>Try again</Text>
</TouchableOpacity>
</View>
);
};
//Login
const Login = () => {
const reuqestedByUser = () => {
return null;
};
};
My question is... Is there any way to call the reuqestedByUser() of Login or any other component function from Error screen action.

Yes, since ErrorNotification is the parent of Login, It can pass reuqestedByUser to it as props.
Else, You can make use of the context API

Related

TypeError: undefined is not an object (evaluating 'item.element')

I'm working on a react native project and expo v.~45.0.0, I'm working with data from an API that I list on my main screen using a FlatList. I've tried passing the api element I'm referencing from a lower file to a higher one. and I get this result TypeError: undefined is not an object (evaluating 'exercise.exercise_name')
//ExerciseListView.js
const ExercisesListView = ({navigation }) =>{
const [data, SetData] = useState([]);
const fetchExercises = async () =>{
const DATA = await getExercisesHome();
SetData(DATA)
};
useEffect(() =>{
fetchExercises();
}, []);
const renderItem = ({ item }) =>{
return(
<ExerciseButton
exercise = {item}
navigation={navigation}
/>
)
}
return(
<FlatList
// ref = {(ref) => { flatListRef = ref; }}
data = {data}
initialNumToRender ={7}
keyExtractor = {(item) => item.id_ejercicios }
renderItem = { memoizedData }
/>
)
}
//App.js
const App = ({exercise, navigation}) => {
return(
<DataExercise
exercise={exercise}
/>
}
//DataExercise.js
import React from 'react'
import { Text, View } from 'react-native'
import {styles} from '../Styles/LayoutModal'
const DataExercise = ({exercise}) => {
return (
<View exercise={exercise}>
<Text style={styles.textTouchExercise}>{exercise.nombre_ejercicio}</Text>
<Text style={styles.textTouchExercise}>{"Grupo muscular:
"+exercise.grupo_muscular}</Text>
</View>
)
}
export default DataExercise
So far so good, but now what I'm trying to do is take my property "exercise.exercise_name" to a file that is at a higher level. Outside the component rendering with the FlatList.
//DoExercise
const DoExercise = ({navigation, exercise}) =>{
return(
<DataExercise <--- *Here I get the error*
exercise={exercise}
/>
)
}
As already noted, my intention is to render the component with the properties of my DataExercise However, the property is obtained in an Undefined way. What can I do so that my properties can also be resided in DoExercise.js? Is there a way that I can define global properties? How is it done?

how to display a state which is passed as an object to another screen in react native

So I have created a state like :
const [inState, setInState] = useState([<View />]);
Then on click of some buttons, I am updating inState
const breakOutClick = () => {
setInState([
...inState,
<>
<StatusBoxComponent
ImageConfigIconCheckOut={true}
status={'Break-Out'}
time={time}
/>
</>,
]);
};
const breakInClick = () => {
setInState([
...inState,
<>
<StatusBoxComponent
ImageConfigIconCheckOut={true}
status={'Break-In'}
time={time}
/>
</>,
]);
};
I am able to display everything stored in inState, on this same screen in this manner:
<View>
{inState}
</View>
I am passing this inState to another screen in the following manner:
props.navigation.navigate(ChartScreen, {
inState: Object.assign({}, inState),
});
Then on this second screen, i.e, ChartSCreen, I did the following:
const ChartScreen = (props: any) => {
const {inState} = props.route.params;
useEffect(() => {
console.log('>>>>>>>>>', inState);
}, []);
return (
<View>
{inState} // getting error here
</View>
);
};
I have console the inState, which looks like this:
{
"0": <ForwardRef />,
"1": <React.Fragment>
<StatusBoxComponent
ImageConfigIconCheckOut={true}
status="Break-In"
time="17:51:40"
/>
</React.Fragment>,
"2": <React.Fragment>
<StatusBoxComponent
ImageConfigIconCheckOut={true}
status="Break-Out"
time="17:51:42"
/>
</React.Fragment>
}
How can I display the multiple StatusBoxComponent on my second screen?
You are displaying initially an array, but by calling Object.assign({}, inState) you're creating an object. Where you're getting the error, you're attempting to render that object, not an array of components. Try using Object.values to get only the values and virtually "restore" your initial array.
const ChartScreen = (props: any) => {
const {inState} = props.route.params;
useEffect(() => {
console.log('>>>>>>>>>', inState);
}, []);
return (
<View>
{Object.values(inState)} // getting error here
</View>
);
};

How to set parent state from FlatList component?

I have a PaymentMethodsScreen screen. On this screen there is a FlatList with PaymentCardItem components inside. And there is a checkbox inside the PaymentCardItem. When this checkbox checked I would like to update selectedCardToken state of PaymentMethodsScreen. But unfortunately I couldn't figure out how to do it. I tried to pass props but I was doing it wrong. Here is my code (without passing props).
How can I achieve that? Thank you very much for your helps.
const PaymentCardItem = ({ family, association, bin_number, token, isSelected }) => (
<View>
<RadioCheckbox
selected={ isSelected }
onPress={ () => this.setSelectedCardToken(token) // Something wrong here }
/>
<Text>{family}, {association}</Text>
<Text>{bin_number}**********</Text>
</View>
);
const PaymentMethodsScreen = ({navigation}) => {
const {state} = useContext(AuthContext);
const [cardList, setCardList] = useState(null) // This stores card list data from API request
const [selectedCardToken, setSelectedCardToken] = useState('test token')
const renderItem = ({ item }) => (
<PaymentCardItem
bin_number={item.bin_number}
family={item.family}
association={item.association}
token={ item.token }
isSelected={ (selectedCardToken == item.token) }
/>
);
return (
<SafeAreaView>
<View>
<FlatList
data={cardList}
renderItem={renderItem}
keyExtractor={item => item.alias}
/>
</View>
</SafeAreaView>
);
};
add onPress prop to PaymentCardItem:
// PaymentMethodsScreen
<PaymentCardItem
onPress={() => setSelectedCardToken(item.token)}
>
I don't know how the PaymentCardItem component is structured, but generally you should add onPress prop on the TouchableOpacity in the component or whatever is your onPress handler:
// PaymentCardItem component
<TouchableOpacity
onPress={() => props.onPress()}
>
You can pass down the handler function which gets called on checkbox being checked or unchecked to your PaymentCardItem component.
You can also pass setSelectedCardToken directly, but in case you have some extra logic before you update state, it's better to have a handler for more readability.
So, the code will be like below.
const PaymentMethodsScreen = ({ navigation }) => {
const { state } = useContext(AuthContext);
const [cardList, setCardList] = useState(null) // This stores card list data from API request
const [selectedCardToken, setSelectedCardToken] = useState('test token')
const handleCardTokenSelection = (isTokenSelected) => {
if(isTokenSelected) {
setSelectedCardToken(); // whatever logic you have
} else {
setSelectedCardToken(); // whatever logic you have
}
}
const renderItem = ({ item }) => (
<PaymentCardItem
bin_number={item.bin_number}
family={item.family}
association={item.association}
token={ item.token }
isSelected={ (selectedCardToken == item.token) }
handleCardTokenSelection={handleCardTokenSelection}
/>
);
return (
<SafeAreaView>
<View>
<FlatList
data={cardList}
renderItem={renderItem}
keyExtractor={item => item.alias}
/>
</View>
</SafeAreaView>
);
};
const PaymentCardItem = ({ family, association, bin_number, token, isSelected, handleCardTokenSelection }) => (
<View>
<RadioCheckbox
selected={ isSelected }
onPress={handleCardTokenSelection}
/>
<Text>{family}, {association}</Text>
<Text>{bin_number}**********</Text>
</View>
);
You need to set the state for PaymentCardItem not for the whole Flatlist, to show the item is selected.
I think you update the PaymentCardItem component to something like the below code(You can update the logic as per requirement)
class PaymentCardItem extends React.Component {
constructor(props) {
super(props);
this.state = {selectedCardToken: "", isSelected: false};
}
setSelectedCardToken=(token)=>{
if(selectedCardToken == token){
this.setState({
selectedCardToken: token,
isSelected: true
})
}
}
render() {
const { family, association, bin_number, token }=this.props;
const { isSelected } = this.state;
return (
<View>
<RadioCheckbox
selected={ isSelected }
onPress={ () => this.setSelectedCardToken(token)
/>
<Text>{family}, {association}</Text>
<Text>{bin_number}**********</Text>
</View>
);
}
}

"Rendered more hooks than during the previous render" error when the initial value for the hook is a query result from a database

I'm using React's hook and I want to have a value that is retrieved from the database as the initial value. However, I'm getting the following error:
Invariant Violation: Invariant Violation: Rendered more hooks than
during the previous render.
const { data, loading, error } = useQuery(GET_DATA)
const { initialValue } = data
const [value, setValue] = useState(initialValue)
I'm using the React Apollo hook.
Update
export default NotificationScreen = ({ navigation }) => {
const { data: initialNotificationSettings, loading: loadingInitialSettings, error: initialSettingsError } = useQuery(GET_NOTIFICATION_SETTINGS)
if (loadingInitialSettings) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#FF5D4E" />
</View>
)
}
if (initialSettingsError) return <Text>Error...</Text>
const {
borrowerLendingNotificationToken,
} = initialNotificationSettings.me
const [borrowerPending, notifyBorrowerPending] = useState(borrowerLendingNotificationToken)
return (
<SafeAreaView style={styles.container}>
</SafeAreaView>
)
}
The problem is that you are using hook below return. Try to update
export default NotificationScreen = ({ navigation }) => {
const { data: initialNotificationSettings, loading: loadingInitialSettings, error: initialSettingsError } = useQuery(GET_NOTIFICATION_SETTINGS)
const [borrowerPending, notifyBorrowerPending] = useState("")
useEffect(() => {
if (initialNotificationSettings.me) {
const { borrowerLendingNotificationToken } = initialNotificationSettings.me
notifyBorrowerPending(borrowerLendingNotificationToken);
}
}, [initialNotificationSettings]);
if (loadingInitialSettings) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#FF5D4E" />
</View>
)
}
if (initialSettingsError) return <Text>Error...</Text>
return (
<SafeAreaView style={styles.container}>
</SafeAreaView>
)
}

React Native: Append Data To Prop In mapStateToProps

I have prop called "data" that it populated with records of data initially to the state on application load. What I trying to do is append to the "data" prop based on a function that I am firing in mapDispatchToProps.
Code is currently as follows:
class ListScreen extends React.Component {
static navigationOptions = {
header: null,
};
static propTypes = {
navigation: PropTypes.object.isRequired,
data: PropType.isRequired,
nextBatchOfData: PropTypes.func,
};
getData = () => {
this.props.nextBatchOfData({ some, search, params });
};
render() {
const { data } = this.props;
return (
<View style={styles.container}>
<ScrollView>
<StatusBar barStyle="default" />
<FlatList
data={data}
keyExtractor={({ id }) => `${id}`}
numColumns={2}
/>
</ScrollView>
</View>
);
}
}
const mapStateToProps = state => {
return {
data: [...dataSelectors.getOfferResults(state)], // Attempting to append to existing state..
};
};
const mapDispatchToProps = dispatch => ({
nextBatchOfData: searchParams => dispatch(actions.dataSearch.request(searchParams)),
});
When run the getData() function, the nextBatchOfData() prop will fire. However, the previous state gets overwritten. Is there any way to get around this?
Here is a section of my reducer where I am getting back the data:
[combineActions(actions.dataSearch.success, actions.dataSearch.fail)]: (
state,
{ payload, meta, error }
) => {
const hasResults = payload && !error;
return {
...state,
isLoading: false,
error: error && payload.message,
results: hasResults ? payload : state.results,
};
I believe the best way to do this is on the reducer.
You will have state available so the append will be more natural to be done there.

Resources