0

I am new to React Native and don't quite understand the concept of initial states of an object and updating the state when I have more than one property to set.

the error (edit #2):

Objects are not valid as a React child (found: object with keys {userRole}). If you meant to render a collection of children, use an array instead.

App.js

const initialLoginState = {
    userRole: null,
    userId: null,
};

const [user, setUser] = useState(initialLoginState);
const [isReady, setIsReady] = useState(false);

const restoreUser = async () => {
    const user = await authStorage.getUser();
    if (user) setUser(user);
};

if (!isReady) {
    return (
        <AppLoading
            startAsync={restoreUser}
            onFinish={() => setIsReady(true)}
            onError={console.warn}
        />
    );
}

//render
return (
    <AuthContext.Provider value={{ user, setUser }}>
        <NavigationContainer>
            {user.userRole ? <ViewTest /> : <AuthNavigator />}
        </NavigationContainer>
    </AuthContext.Provider>
);

useAuth which updates the user when I received the data:

const logIn = (data, authToken) => {
        setUser((prevState) => ({
            userRole: {
                ...prevState.userId, 
                userRole: data.USERROLE,
            },
        }));
        authStorage.storeToken(data.USERID);
    };
denistepp
  • 489
  • 7
  • 23
  • 1
    Can you clarify what you mean with `initialise first state`? – Federkun Feb 14 '21 at 14:36
  • I want my state object to have properties as shown above in `initialLoginState` so I can use it to conditionally render the component. – denistepp Feb 14 '21 at 14:38
  • Something similar when setting the counter like `const [counter, setCounter] = useState(0)` and then `setCounter(counter + 1)` to increment, but I need it with an object that has properties – denistepp Feb 14 '21 at 14:39
  • if your issue just that `setUser` return an error because `prevState.userId` can be null? the rest is fine – Federkun Feb 14 '21 at 14:40
  • Does this answer your question? [React Hooks useState() with Object](https://stackoverflow.com/questions/54150783/react-hooks-usestate-with-object) – P.hunter Feb 14 '21 at 14:41
  • my issue is with the initial useState. For some reason I get an error that I can't use it like `const [user, setUser] = useState(initialLoginState)`. Undefined for user.userRole when rendering an object – denistepp Feb 14 '21 at 14:43
  • in you case you probably want ```setUser({...user, userID: data.USERID})``` – P.hunter Feb 14 '21 at 14:43
  • can you please post the error here – P.hunter Feb 14 '21 at 14:44
  • 1
    yes, ill modify the question to be more clear now. – denistepp Feb 14 '21 at 14:45
  • 1
    you may want to share more code, in particular where `user` is coming from – Federkun Feb 14 '21 at 14:47
  • @Federkun updated the whole question, it is the problem with the `setUser` – denistepp Feb 14 '21 at 15:02
  • yeah, i assume you have the issue on log in? `setUser(user => ({ ...user, userId: data.USERID, userRole: data.USERROLE }))` - check other places where you modify the user's state as well. For example, `authStorage.getUser()` may return just the user id, not the full object that you expect in the rest of the app – Federkun Feb 14 '21 at 15:07
  • I return the whole user object and it is not modified anywhere else. May it be in the setUser when I only update one property? – denistepp Feb 14 '21 at 15:15

3 Answers3

1

You don't need prevState in functional component. user is the prevState before you set new state

const logIn = (data, authToken) => {
  setUser({...user, userRole: data.USERROLE});
  authStorage.storeToken(data.USERID);
};
Erdenezaya
  • 141
  • 4
  • Slightly different error, but still... `Objects are not valid as a React child (found: object with keys {0, 1, userRole}). If you meant to render a collection of children, use an array instead.` – denistepp Feb 14 '21 at 15:14
  • I think the problem is within 2 components you are rendering. Can you share component? – Erdenezaya Feb 14 '21 at 15:18
  • it is not the problem even if I replace it with `View` of different colors. The screens won't change. The problem is somewhere in Context... – denistepp Feb 14 '21 at 15:19
1

Objects are not valid as a React child (found: object with keys {userRole}). If you meant to render a collection of children, use an array instead.

    <AuthContext.Provider value={{ user, setUser }}>  // <---- the problem is here
        <NavigationContainer>
            {user.userRole ? <ViewTest /> : <AuthNavigator />}
        </NavigationContainer>
    </AuthContext.Provider>

I'm not sure what AuthContext.Provider is, but it's trying to render the object(User) as html react elements, make sure you know what sort of data the value prop of that component takes.

P.hunter
  • 1,345
  • 2
  • 21
  • 45
  • Context provider's value allows me to see the changes in the value property. setUser is accessible across components if I use `useContext`. But you are right, if I use different values to render the screen, it works fine... – denistepp Feb 14 '21 at 15:21
1

I was able to get the right answer with the help of @P.hunter, @Erdenezaya and @Federkun.

The problem was in the state init and setUser().

  1. App.js
    const initialLoginState = {
        userRole: null,
        userId: null,
    };

    const [user, setUser] = useState({
        initialLoginState,
    });
    const [isReady, setIsReady] = useState(false);

    const restoreUser = async () => {
        const user = await authStorage.getUser();
        if (user) setUser(user);
    };

    if (!isReady) {
        return (
            <AppLoading
                startAsync={restoreUser}
                onFinish={() => setIsReady(true)}
                onError={console.warn}
            />
        );
    }

    //syntax error was found in {user.userRole}
    return (
        <AuthContext.Provider value={{ user, setUser }}>
            <NavigationContainer>
                {user.userRole ? <ViewTest /> : <AuthNavigator />}
            </NavigationContainer>
        </AuthContext.Provider>
    );
  1. Context functionality for setting the user had to be done like this:
export default useAuth = () => {
    const { user, setUser } = useContext(AuthContext);

    const logIn = (data, authToken) => {

        setUser({ ...user, userRole: data.USERROLE });

        authStorage.storeToken(data.USERID);
    };

    const logOut = () => {
        setUser({ ...user, userRole: null });
        authStorage.removeToken();
    };

    return { user, logIn, logOut };
};

Thank you all for your help!

denistepp
  • 489
  • 7
  • 23