Reputation: 117
I'm new to React Native and trying to build a simple app where the user can change their user info. My problem starts when I'm trying to load the already existing data into my form, sometimes I'm getting this error: undefined is not an object result[0].first_name, but not always.
thanks in advance
here's my code:
//Action
import { FETCH_USER, UPDATE_USER } from './actionTypes';
export const fetchUser = () => {
return async (dispatch, getState) => {
const url = 'https://reqres.in/api/users?page=1';
const response = await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
});
if(!response.ok){
const errorResData = await response.json();
console.log('error', response);
let message = "error";
throw new Error(message);
}
const resData = await response.json();
dispatch({ type: FETCH_USER, data: resData.data });
};
};
my reducer
import { FETCH_USER } from '../actions/actionTypes';
const initialState = {
data: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case FETCH_USER:
return {
data: action.data
};
default:
return state;
}
};
component
import { useDispatch, useSelector } from 'react-redux';
import Style from '../../constants/Style';
import Input from '../../components/UI/Input/Input';
import Button from '../../components/UI/Button/Button';
import Textarea from '../../components/UI/Textarea/Textarea';
import * as userActions from '../../store/actions/user';
const FORM_INPUT_UPDATE = 'FORM_INPUT_UPDATE';
const FROM_INITIAL_VALUE = 'FROM_INITIAL_VALUE';
const formReducer = (state, action) => {
if (action.type === FORM_INPUT_UPDATE) {
const updatedValues = {
...state.inputValues,
[action.input]: action.value,
};
const updatedValidities = {
...state.inputValidties,
[action.input]: action.isValid,
};
let updatedFormIsValid = true;
for (const key in updatedValidities) {
updatedFormIsValid = updatedFormIsValid && updatedValidities[key];
}
return {
formIsValid: updatedFormIsValid,
inputValidties: updatedValidities,
inputValues: updatedValues,
};
}
if (action.type === FROM_INITIAL_VALUE) {
return {
...state,
inputValues: action.initialValues,
};
}
return state;
};
const ChangeAccount = props => {
const [error, setError] = useState();
const [refresh, setRefresh] = useState(false);
const dispatch = useDispatch();
const result = useSelector(state => {
return state.user.data.filter(state => state.id == 1);
});
const [formState, dispatchFormState] = useReducer(formReducer, {
inputValues: {
firstname: '',
lastname: '',
email: '',
password: '',
passwordAgain: '',
},
inputValidties: {
firstname: false,
lastname: false,
email: false,
password: false,
passwordAgain: false,
},
formIsValid: false,
});
const inputChangeHandler = useCallback(
(inputIdentifier, inputValue, inputValidity) => {
dispatchFormState({
type: FORM_INPUT_UPDATE,
value: inputValue,
isValid: inputValidity,
input: inputIdentifier,
});
},
[dispatchFormState]
);
const loadUser = useCallback(async () => {
setError(null);
try {
await dispatch(userActions.fetchUser()).then(() => {
setRefresh(false);
});
} catch (err) {
setError(err.message);
}
}, [dispatch, setError]);
const saveHandler = useCallback(async() => {
alert(formState.inputValues.firstname);
});
useEffect(() => {
setRefresh(true);
loadUser();
}, []);
useEffect(() => {
console.log('loadedData', result);
my error points to the code below
dispatchFormState({
type: FROM_INITIAL_VALUE,
initialValues: {
firstname: result[0].first_name,
lastname: result[0].last_name,
email: result[0].email,
},
});
}, [refresh, dispatchFormState]);
if (!refresh) {
return (
<View style={{ flex: 1, backgroundColor: Style.Colors.lightWhite }}>
<View style={styles.header}>
<Textarea Color={'rgb(77,77,77)'} Size={29.5}>
Settings
</Textarea>
</View>
<View style={styles.heading}>
<Textarea Color={'rgb(77,77,77)'} Size={22.1}>
Account
</Textarea>
</View>
<View
style={{
width: wp('100%'),
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 30,
height: hp('40%'),
}}>
<Input
style={Style.lightInputStyle}
id="firstname"
label="Firstname"
labelColor="rgb(157,157,186)"
errorText="Check firstname"
autoCapitalize="none"
autoCorrect={false}
keyboardType="default"
onInputChange={inputChangeHandler}
initialValue={formState.inputValues.firstname}
initiallyValid={false}
required
/>
<Input
style={Style.lightInputStyle}
id="lastname"
label="Lastname"
labelColor="rgb(157,157,186)"
errorText="Check lastname"
autoCapitalize="none"
autoCorrect={false}
keyboardType="default"
onInputChange={inputChangeHandler}
initialValue={formState.inputValues.lastname}
initiallyValid={false}
required
/>
<Input
style={Style.lightInputStyle}
id="email"
label="Email"
labelColor="rgb(157,157,186)"
errorText="Check email"
autoCapitalize="none"
autoCorrect={false}
keyboardType="default"
onInputChange={inputChangeHandler}
initialValue={formState.inputValues.email}
initiallyValid={false}
required
/>
<Input
style={Style.lightInputStyle}
id="password"
label="Password"
labelColor="rgb(157,157,186)"
errorText="Check password"
autoCapitalize="none"
autoCorrect={false}
keyboardType="default"
onInputChange={inputChangeHandler}
required
/>
<Input
style={Style.lightInputStyle}
id="passwordAgain"
label="Password (again)"
labelColor="rgb(157,157,186)"
errorText="Check password (again)"
autoCapitalize="none"
autoCorrect={false}
keyboardType="default"
onInputChange={inputChangeHandler}
required
/>
</View>
<View
style={{
width: wp('100%'),
alignItems: 'center',
marginTop: hp('10%'),
}}>
<Button
BtnStyle={Style.lightPurpleButton}
Color={Style.Colors.lightWhite}
Size={Style.FontSize.btn}
OnPress={saveHandler}>
Save
</Button>
</View>
</View>
);
} else {
return (
<View style={{ flex: 1, backgroundColor: Style.Colors.lightWhite }}>
<Text>Loading</Text>
</View>
);
}
};
const styles = StyleSheet.create({
header: {
marginTop: 20,
marginLeft: '7%',
},
heading: {
marginTop: 30,
marginLeft: '7%',
},
});
export default ChangeAccount;
Upvotes: 1
Views: 335
Reputation: 25
It seems to me that you're not checking your result and specifically you're not checking if result[0] is populated when you trigger the dispatchFormState, you're probably trying to populate the form before your result is actually loaded. Also, you should check your refresh variable in order to perform the dispatch only when data is not refreshing.
Try this:
useEffect(() => {
console.log('loadedData', result);
if (!refresh && result[0]) {
dispatchFormState({
type: FROM_INITIAL_VALUE,
initialValues: {
firstname: result[0].first_name,
lastname: result[0].last_name,
email: result[0].email,
},
});
}
}, [refresh, dispatchFormState]);
Upvotes: 1