Rabbit
Rabbit

Reputation: 117

React Native TypeError undefined is not an object

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.

here's my snack

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

Answers (1)

Chiara Marello
Chiara Marello

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

Related Questions