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;
Related
I'm trying to use a custom hook that bring me functions to handle my TODOS on my context, but it gives me an error
Uncaught TypeError: useContext(...) is undefined
The above error occurred in the component:
Complete Error Image
TodoProvider.jsx
import { useReducer } from 'react';
import { useTodos } from '../hooks/useTodos';
import { TodoContext, todoReducer } from './';
export const TodoProvider = ({ children }) => {
const init = () => {
return [];
};
const [todos, dispatchTodos] = useReducer(todoReducer, {}, init);
const { handleNewTodo, handleToggleTodo } = useTodos();
return (
<TodoContext.Provider
value={{ todos, dispatchTodos, handleNewTodo, handleToggleTodo }}
>
{children}
</TodoContext.Provider>
);
};
useTodos.js
import { useContext } from 'react';
import { TodoContext } from '../context';
import { types } from '../types/types';
export const useTodos = () => {
const { dispatchTodos } = useContext(TodoContext);
const handleNewTodo = todo => {
const action = {
type: types.add,
payload: todo,
};
dispatchTodos(action);
};
const handleToggleTodo = id => {
dispatchTodos({
type: types.toggle,
payload: id,
});
};
return { handleNewTodo, handleToggleTodo };
};
The error traceback in your image says
`useContext(...)` is not defined
useTodos (useTodos.js:6)
Since you aren't showing your useTodos.js file, I must rely on my crystal ball to tell me that you've forgotten to
import {useContext} from 'react';
in useTodos.js, hence "not defined".
Here's an one-file example based on your code that verifiably does work...
import { useReducer, useContext, createContext } from "react";
function todoReducer(state, action) {
switch (action.type) {
case "add":
return [...state, { id: +new Date(), text: action.payload }];
default:
return state;
}
}
const TodoContext = createContext([]);
const TodoProvider = ({ children }) => {
const [todos, dispatchTodos] = useReducer(todoReducer, null, () => []);
return (
<TodoContext.Provider value={{ todos, dispatchTodos }}>
{children}
</TodoContext.Provider>
);
};
function useTodoActions() {
const { dispatchTodos } = useContext(TodoContext);
function handleNewTodo(todo) {
dispatchTodos({
type: "add",
payload: todo
});
}
function handleToggleTodo(id) {
dispatchTodos({
type: "toggle",
payload: id
});
}
return { handleNewTodo, handleToggleTodo };
}
function useTodos() {
return useContext(TodoContext).todos;
}
function TodoApp() {
const todos = useTodos();
const { handleNewTodo } = useTodoActions();
return (
<div>
{JSON.stringify(todos)}
<hr />
<button onClick={() => handleNewTodo((+new Date()).toString(36))}>
Add todo
</button>
</div>
);
}
export default function App() {
return (
<TodoProvider>
<TodoApp />
</TodoProvider>
);
}
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>
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?
I am trying to create a context for saveing data from an Api response. But it gives me an error.
I don't know how to solve the error.
This screen is a show screen. In the index screen I can use another context and it works well.
I want to create another context for saving data from other Api call. I am trying to copy it but it gives me an error.
Thanks.
TypeError: undefined is not an object (evaluating '_useContext.state')
CONTEXT
import createDataContext from './createDataContext';
import jsonServer from '../api/api';
const currencyPrices = (state, action) => {
switch (action.type) {
case 'show_prices':
return action.payload;
default:
return state;
}
};
const showPrices = (dispatch) => {
return async (id) => {
const response = await jsonServer.get(`/prices/${id}`);
dispatch({type: 'show_prices', payload: response.data[0]});
};
};
export const {Context, Provider} = createDataContext(
currencyPrices,
{showPrices},
[],
);
CREATE DATA CONTEXT
import React, {useReducer} from 'react';
export default (reducer, actions, initialState) => {
const Context = React.createContext();
const Provider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState);
// actions === { addBlogPost: (dispatch) => { return () => {} } }
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{state, ...boundActions}}>
{children}
</Context.Provider>
);
};
return {Context, Provider};
};
SCREEN
/* eslint-disable react-hooks/rules-of-hooks */
import React, {useContext, useEffect} from 'react';
import {View, Text} from 'react-native';
import {Context} from '../context/currencyPrices';
const showScreen = ({navigation}) => {
const {state, showPrices} = useContext(Context);
useEffect(() => {
showPrices(1);
const listener = navigation.addListener('didFocus', () => {
showPrices(1);
});
return () => {
listener.remove();
};
});
return (
<View>
<Text>{state.length}</Text>
</View>
);
};
export default showScreen;
in app.js
import {Provider as ProviderName}
<ProciderName>
<App/>
</ProviderName}
you should wrap provider in mani root like app.js
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.