React Native Context rendering a blank screen when wrapped inside <Provider> - reactjs

I'm trying to build a simple blog native app using context and have stumbled upon an issue to which I can't find a root to.
Here's the structure of it:
/context/createDataContext.js file:
import React, { useReducer } from "react";
export default (reducer, actions, initialState) => {
const Context = React.createContext();
const Provider = ({ childern }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const boundActions = {};
for (let key in boundActions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{childern}
</Context.Provider>
);
};
return { Context, Provider };
};
/context/BlogContext.js:
import createDataContext from "./createDataContext";
const blogReducer = (state, action) => {
switch (action.type) {
case "add_blogpost":
return [...state, { title: `Blog Post Number ${state.length + 1}` }];
default:
return state;
}
};
const addBlogPost = (dispatch) => {
return () => {
dispatch({ type: "add_blogpost" });
};
};
export const { Context, Provider } = createDataContext(
blogReducer,
{ addBlogPost },
[]
);
/screens/IndexScreen.js :
import React, { useContext } from "react";
import { View, Text, StyleSheet, FlatList, Button } from "react-native";
import { Context } from "../context/BolgContext";
const IndexScreen = () => {
const { state, addBlogPost } = useContext(Context);
return (
<View>
<Button title="Add a blod post" onPress={addBlogPost} />
<FlatList
data={state}
keyExtractor={(blogPost) => blogPost.title}
renderItem={({ item }) => {
return <Text>{item.title}</Text>;
}}
/>
</View>
);
};
const styles = StyleSheet.create({});
export default IndexScreen;
And finally App.js :
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import IndexScreen from "./src/screens/IndexScreen";
import { Provider } from "./src/context/BolgContext";
import React from "react";
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
{
<Provider>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={IndexScreen}
options={{ title: "My app" }}
/>
</Stack.Navigator>
</Provider>
}
</NavigationContainer>
);
}
Now I did some debugging, even though the code does't come back with any error, but the issue seems to be on my Provider, since if I remove it I can see content on the screen. Does anybody know why this happens.
Thanks a lot!

You need to change the Provider method like below
Form
const Provider = ({ childern }) => {
To
const Provider = (props) => {
Then you can destructure while passing to the content.provider like below
<Context.Provider value={{ state, ...boundActions }}>
{props.childern}
</Context.Provider>

Related

React Native render error with _default method

My App is crashing due to the following error but i don´t have enough information for my self to solve the problem.
Render Error, Element type is invalid: expected a string (for built-in components) or a class/function (for composite components but got undefined)
Check the render method of _default
I assume it has something to do with some export or import in my code but i am not sure what is that is causing this issue.
createDataContext.js:
import React, { useReducer } from 'react';
export default (reducer, actions, initialState) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
);
};
return { Context, Provider };
};
BlogContext.js:
import createDataContext from './createDataContext';
const blogReducer = (state, action) => {
switch (action.type) {
case 'add_blogpost':
return [...state, { title: `Blog Post #${state.length + 1}` }];
default:
return state;
}
};
const addBlogPost = dispatch => {
return () => {
dispatch({ type: 'add_blogpost' });
};
};
export const { Context, Provider } = createDataContext(
blogReducer,
{ addBlogPost },
[]
);
IndexScreen.js:
import React, { useContext } from 'react';
import { View, Text, StyleSheet, FlatList, Button } from 'react-native';
import { Context } from '../context/BlogContext';
const IndexScreen = () => {
const { state, addBlogPost } = useContext(Context);
return (
<View>
<Text>Index Screen</Text>
<Button title="Add Post" onPress={addBlogPost} />
<FlatList
data={state}
keyExtractor={blogPost => blogPost.title}
renderItem={({ item }) => {
return <Text>{item.title}</Text>;
}}
/>
</View>
);
};
const styles = StyleSheet.create({});
export default IndexScreen;

How to pass function from Context to App.js in React Native?

When I run the app, the error message shows that TypeError: undefined is not an object (evaluating '_useContext.state')
Here is my App.js:
import React from 'react'
import { StatusBar } from 'expo-status-bar';
import { useState, useEffect, useContext } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Context as ActivitiesContext, Provider } from "./context/ActivitiesContext";
const App = () => {
const { state, getActivity } = useContext(ActivitiesContext);
useEffect(() => {
getActivity()
}, []);
return (
<View style={styles.container}>
<Text>Open App.js to start working on your file</Text>
<StatusBar style="auto" />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default App
Here is my ActivitiesContext.js:
import createDataContext from "./CreateDataContext";
const getActivity = () => {
return console.log("hi");
};
export const { Provider, Context } = createDataContext(
{
getActivity,
}
);
Here is my CreateDataContext.js
import React, { useReducer } from 'react';
export default (reducer, action, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in action) {
boundActions[key] = action[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
)
};
return { Context: Context, Provider: Provider };
};
I am expecting that the console.log("hi") from the function getActivity be passed to the App.js and console log "hi".
There also is the Provider, what is its purpose and how do I use it? I think that's what I am missing.
Create context
Use Provider to pass a value by wrapping an component to provide access for components deeper in the tree
Use useContext to access the value from any of the components
const ThemeContext = React.createContext(); // 1.
const App = () => {
// 2.
return <ThemeContext.Provider value={{color: 'black', getColor: () => 'red'}}>
<MyComponet />
</ThemeContext.Provider>
}
const MyComponet = () => {
return (
<View>
<MyButton />
</View>
);
}
const MyButton = () => {
const theme = useContext(ThemeContext) // 3.
return <Button title="Button" color={theme.getColor()}/>;
}

dispatch usereducer in gatsby

I am new to gatsby and hope you can all helt me.
I am getting a typeerror: dispatch is not a function message, and have stuggled with finding my typo for two days now. I am trying to make a cart and need to pass the product to a global state. It is only when I set in the dispatch in my handleClick function I seem to get the problem (function works with only a console.log and takes in the product fine)
This is my code. First the provider, then the layout and last the template that holds a single product:
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.item];
default:
throw new Error(`unknown action ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const [ state, dispatch ] = useReducer(reducer, [])
return (
<CartDispatchContext.Provider value={dispatch}>
<CartStateContext.Provider value={state}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
)
};
export const useCart = () => useContext(CartStateContext);
export const useDispatchCart = () => useContext(CartDispatchContext);
/* layout component */
import React, { useState } from 'react';
import Container from './Container';
import Footerone from './Footerone/Footerone';
import HeaderOne from './HeaderOne/HeaderOne';
import ShopHeaderOne from './ShopHeaderOne/ShopHeaderOne';
import { CartContext } from './CartContext';
import { CartProvider } from './Cart'
export default ({ children }) => {
const[cartVisible, setCartVisible] = useState(false);
const toggleCart = () => {
setCartVisible(!cartVisible)
};
return (
<Container>
<CartProvider>
<CartContext.Provider value={{ cartVisible, toggleCart}}>
<HeaderOne />
<ShopHeaderOne />
{ children }
<Footerone />
</CartContext.Provider>
</CartProvider>
</Container>
)
}
/* singleproduct */
import React from 'react'
import Layout from "../Components/Layout"
import './templates.scss'
import { useDispatchCart } from '../Components/Cart';
export default ( props ) => {
const product = props.pageContext
const dispatch = useDispatchCart();
const handleClick = (item) => {
dispatch({ type: "ADD", item })
}
console.log(product)
return (
<Layout>
<section className="single-grid">
<h1>{product.headline}</h1>
<img src={product.image.mediaItemUrl} alt={product.image.altText}/>
<p>{product.price}</p>
<button onClick={handleClick(product)}>Føj til kurv</button>
</section>
</Layout>
)
}
Have you tried binding directly the dispatch?
return (
<Layout>
<section className="single-grid">
<h1>{product.headline}</h1>
<img src={product.image.mediaItemUrl} alt={product.image.altText}/>
<p>{product.price}</p>
<button onClick={()=> dispatch({ type: "ADD", item })}>Føj til kurv</button>
</section>
</Layout>
)
If the issue persists, check the reducer in order to see if it's being set properly.

Context evaluating as undefined in React Native app

I'm attempting to create an Auth context file which, upon app load, checks if a user is signed in.
To do this, I'm using a 'helper' function which allows me to import the initialisation of the context and just build upon that with additional functions which authorise a user.
However, upon every app load, the Context is returning as 'undefined', and it says 'evaluating _useContext.trySignIn'.
For reference, here is my Context file:
import createDataContext from './createDataContext';
import { AsyncStorage } from 'react-native';
import { navigate } from '../navigationRef';
import { Magic } from '#magic-sdk/react-native';
const m = new Magic('API_key');
const authReducer = (state, reducer) => {
switch (action.type) {
default:
return state;
}
};
const trySignIn = dispatch => async () => {
const isLoggedIn = await m.user.isLoggedIn();
if (isLoggedIn === true) {
navigate('Dashboard');
} else {
navigate('loginFlow');
}
};
export const { Provider, Context } = createDataContext (
authReducer,
{ trySignIn },
{ isLoggedIn: null }
);
Here is my 'createDataContext' file:
import React, { useReducer } from 'react';
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key].dispatch;
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
)
};
return { Context, Provider }
};
Here is my navigation file:
import { NavigationActions } from 'react-navigation';
let navigator;
export const setNavigator = (nav) => {
navigation = nav;
};
export const navigate = (routeName, params) => {
navigator.dispatch(
NavigationActions.navigate({
routeName, params
})
);
};
And finally, here is my component attempting to use my context:
import React, { useEffect, useContext } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { Context } from '../context/AuthContext';
const LoadingScreen = () => {
const { trySignIn } = useContext(Context);
useEffect(() => {
trySignIn();
}, [])
return (
<View style={styles.mainView}>
<ActivityIndicator style={styles.indicator} />
</View>
)
}
Can anyone see why my context would be returning as 'undefined' in my component?

React Context state not shared between components

I have been using Redux for a long time, but now decided to try out the new ContextAPI.
I got it working with one component/page (using NextJs), however the state isn't shared between pages/components.
store.js
import React, { createContext, useReducer } from 'react';
import reducer from './reducer'
const initialState = {
players: [],
};
const Store = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<Context.Provider value={[state, dispatch]}>
{children}
</Context.Provider>
)
};
export const Context = createContext(initialState);
export default Store;
reducer.js
const Reducer = (state, action) => {
switch (action.type) {
case 'ADD_PLAYER':
return {
...state,
players: [...state.players, action.payload],
};
case 'REMOVE_PLAYER_BY_INDEX':
const array = state.players;
if (array) {
array.splice(action.payload, 1);
}
return {
...state,
players: !array ? [] : array,
};
default:
return state;
}
};
export default Reducer;
add players page /players/add (addplayerspage.js)
import React, { useContext } from 'react';
import map from 'lodash/map';
import isEqual from 'lodash/isEqual';
import { Context } from '../../../context';
const PlayerCreatePage = () => {
const [_, dispatch] = useContext(Context);
const handleAddPlayer = () => {
dispatch({ type: 'ADD_PLAYER', payload: Math.random() });
};
const handleRemovePlayerByIndex = (index) => {
dispatch({ type: 'REMOVE_PLAYER_BY_INDEX', payload: index });
};
return (
<div className="layout">
<div>
<Context.Consumer>
{([state]) => {
const { players } = state;
return map(players, (p, i) => <div
key={i}
onClick={() => handleRemovePlayerByIndex(i)}
>
{p}
</div>
)
}}
</Context.Consumer>
</div>
<button onClick={() => handleAddPlayer()}>Add new</button>
</div>
);
};
export default React.memo(PlayerCreatePage, (prev, next) => isEqual(prev, next));
lobby players page /players/lobby (lobbyplayerspage.js)
import React, { useContext } from 'react';
import map from 'lodash/map';
import { Context } from '../../../context';
const PlayersLobbyPage = () => {
const [state, _] = useContext(Context);
return (
<div>
<div>
{map(state.players, (p, i) => <div
key={i}
>
{p}
</div>
)}
</div>
</div>
);
};
export default PlayersLobbyPage;
_app.js (NextJs)
import App, { Container } from 'next/app';
import '../styles/main.css';
import Store from '../context';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Store>
<Component {...pageProps} />
</Store>
</Container>
);
}
}
export default MyApp;
THE PROBLEM:
Have two tabs open
Add players
Lobby
Add a new player
See that player is added on 'Add players' page
2.See that NOTHING happens on 'Lobby' page
Okay, so the issue is that I was trying to "share" context api state between different open tabs, it doesn't work like that by default, even for redux (tried adding it and faced same result), for redux there's a redux-state-sync library for that, nonetheless I will use sockets in future, so this won't be an issue.
Closed.

Resources