0

I can do this without using createContext(), and useContext(). But I want to learn to use this, so I want to add useContext, and createContext.

Normaly i have this code in my MainScreen.js

 const [users, setUsers] = useState([
        <HorizontalCircles skeleton={true} key={0} colorFirst={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} colorSecond={"rgb(" + 100 + "," + 100 + "," + 100 + ")"}/>,
        <HorizontalCircles skeleton={true} key={1} colorFirst={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} colorSecond={"rgb(" + 100 + "," + 100 + "," + 100 + ")"}/>,
    
      ])
    const getUsers = () => {
        // TODO: get discussion from SERVER
        // Dumy Data
        console.log("Getting Users");
    
        const tmpUsers = [];
    
        for (let i = 0; i < 1; i++) {
          const rand = Math.round(Math.random() * 255);
          const rand2 = Math.round(Math.random() * 255);
          const rand3 = Math.round(Math.random() * 255);
    
          tmpUsers.push(<HorizontalCircles key={i} colorFirst={"rgb(" + rand + "," + rand2 + "," + rand3 + ")"} colorSecond={"rgb(" + rand3 + "," + rand + "," + rand2 + ")"} />)
        }
        setTimeout(() => {
          setUsers(tmpUsers);
        }, 5000);
    
      }

Since my App.js included by navigations, I thought I need to add another file, where i can show my Provider.

Here is App.js

import 'react-native-gesture-handler';
import React from "react";
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import MainScreen from './src/components/screens/MainScreen';
import NewScreen from './src/components/screens/NewScreen';
import AnotherScreen from "./src/components/screens/AnotherScreen";


const Stack = createStackNavigator();

const App = () => {

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home" screenOptions={{headerShown: false}}>
        <Stack.Screen name="Home" component={MainScreen} />
        <Stack.Screen name="NewScreen" component={NewScreen} />
        <Stack.Screen name="AnotherScreen" component={AnotherScreen} />        
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

Here is App2.js where i include Provider,

import React, { createContext,  } from "react";
import { View } from "react-native";
import MainScreen from "./src/components/screens/MainScreen";


export const UserContext = createContext();

function App2() {

  const [users, setUsers] = useState([
    <HorizontalCircles skeleton={true} key={0} colorFirst={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} colorSecond={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} />,
    <HorizontalCircles skeleton={true} key={1} colorFirst={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} colorSecond={"rgb(" + 100 + "," + 100 + "," + 100 + ")"} />,

  ])
  return (
    <View className="App2">
      <UserContext.Provider value={users, setUsers}>
        <MainScreen />
      </UserContext.Provider>

    </View>
  )
}

This is the only change in MainScreen.js after I added App2.js

  const [users, setUsers] = useContext(UserContext);

I left getUsers() function the same.

Whats my mistake? It doesnt work, I just want to learn how to use this, I thought this should have worked, but it doesnt.

it gives me an error, saying : invalid attempt to destructure non-iterable instance. In order to be iterable, non-array objects, must have Symbol.iterator method.

the last error i am getting is:

enter image description here

2 Answers2

0

You are missing the array when assigning the value to context. Thats why you get the error message, you are expeting an array but it is not. So

<UserContext.Provider value={users, setUsers}>

Should be

<UserContext.Provider value={[users, setUsers]}>

But you should also useMemo to avoid unnecessary updates

const context = useMemo(() => ([users, setUsers]), [users])
// ... code

<UserContext.Provider value={context}>
row
  • 181
  • 8
  • Thanks for responding. I did [users,setUsers] but it still gave the same error, maybe because i didnt add useMemo? idk. But im trying to do what the other person told me to do now, It's more close to what I want i am thinking –  May 28 '21 at 12:03
0

For a component to have access to the context values it need to be wrapped with Provider.

Create a new file . UserContextManager

import React, {createContext} from 'react';

export const UserContext = createContext();

function UserContextManager(props) {
  const [users, setUsers] = useState([
    <HorizontalCircles
      skeleton={true}
      key={0}
      colorFirst={'rgb(' + 100 + ',' + 100 + ',' + 100 + ')'}
      colorSecond={'rgb(' + 100 + ',' + 100 + ',' + 100 + ')'}
    />,
    <HorizontalCircles
      skeleton={true}
      key={1}
      colorFirst={'rgb(' + 100 + ',' + 100 + ',' + 100 + ')'}
      colorSecond={'rgb(' + 100 + ',' + 100 + ',' + 100 + ')'}
    />,
  ]);

  return (
    <UserContext.Provider value={{users, setUsers}}>
      {props.children}
    </UserContext.Provider>
  );
}

export default UserContextManager;

In App.js

<UserContextManager>
<NavigationContainer>
<Stack.Navigator initialRouteName="Home" screenOptions={{headerShown: false}}>
  <Stack.Screen name="Home" component={MainScreen} />
  <Stack.Screen name="NewScreen" component={NewScreen} />
  <Stack.Screen name="AnotherScreen" component={AnotherScreen} />        
</Stack.Navigator>
</NavigationContainer>
</UserContextManager>

Now with the above change your MainScreen will have access to the context values.

In your MainScreen.js import the userContext

import { UserContext } from 'your path to UserContextManager';


const Mainscreen = () => {
const { users, setUsers } = useContext(UserContext); 
  
  .... 
}
Shyam
  • 5,292
  • 1
  • 10
  • 20
  • Wow, that was amazing solution, thank you a lot. Now i am going to do what you suggested, im excited to see the output. –  May 28 '21 at 11:53
  • Btw you said "Now wrap your App2 this `UserContextManager`." Well i just added App2.js because i couldnt do it in my App.js as you did by adding `UserContextManager`. But since it is added in App.js, shouldnt I delete App2.js? because i dont have anything to do with it? So i mean i couldnt understand why you said i should wrap my App2 –  May 28 '21 at 12:00
  • 1
    I didn't know you were using App2 for trying to resolve this error Have updated my answer now . Please check – Shyam May 28 '21 at 12:03
  • Thanks. I updated it in my app too. But i am still having an error. Now it says `element type is invalid: expected a string (for built in components) or a class/function (for composite components but got undefined. You likely forgot to export your component from the file its defined in, or you might have mixed up default and named imports . Check the render method of App.` –  May 28 '21 at 12:11
  • 1
    Did you import `UserContextManager` in your App ? – Shyam May 28 '21 at 12:13
  • Yes, i did by `import { UserContextManager } from "./src/context/UserContextManager"; ` the path is correct –  May 28 '21 at 12:14
  • 1
    How are you exporting it ? . – Shyam May 28 '21 at 12:15
  • but there must be problem with it, as you guessed, because when i do ctrl + click, it doesnt take me to UserContextManager.js –  May 28 '21 at 12:16
  • as you coded above, `export default UserContextManager;` exported this way –  May 28 '21 at 12:17
  • 1
    Then you should not be importing it in the way you did it . when you import something within `{}` it means that they are named imports and they are exported like `export UserContextManager` . When you do default export . you should import it without the `{}` . change you import statement to `import UserContextManager from 'your path'` – Shyam May 28 '21 at 12:19
  • I learned this now, about importing. Thanks a lot. But i still have an error, it says `invalid attempt to destructure non iterable instance . In order to be iterable , non array objects must have a [Symbol.iterator]() method` –  May 28 '21 at 12:24
  • 1
    You are getting this error in which file ? – Shyam May 28 '21 at 12:26
  • I really couldnt get which file, it doesnt tell me i think, and i editted my question by adding a pic of the error –  May 28 '21 at 12:30
  • 1
    you should not be reading it like this - `const [users, setUsers]` . you should be reading it as `const { users, setUsers }` . Because in our context provider we are doing this `value={{users, setUsers}}` not `value={[users, setUsers]}` – Shyam May 28 '21 at 12:33
  • Omg... I have too much to learn from you, Thank you a lot!!!Wish you were my team leader or boss :) Can I ask you more questions related to this app? –  May 28 '21 at 12:36
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/233002/discussion-between-shyam-and-helloworld). – Shyam May 28 '21 at 12:37