Context & Provider not working - render is not a function - reactjs

Been trying many things but for I cannot get this working for the life of me. Been looking at various Context tutorials but no. Could you help me out?
App.js
import React from 'react'
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import AccountScreen from './src/screens/AccountScreen'
import SigninScreen from './src/screens/SigninScreen'
import SignupScreen from './src/screens/SignupScreen'
import TrackCreateScreen from './src/screens/TrackCreateScreen'
import TrackDetailScreen from './src/screens/TrackDetailScreen'
import TrackListScreen from './src/screens/TrackListScreen'
import UserProvider from './src/context/appContext'
const switchNavigator = createSwitchNavigator({
loginFlow: createStackNavigator({
Signup: SignupScreen,
Signin: SigninScreen
}),
mainFlow: createBottomTabNavigator({
trackListFlow: createStackNavigator({
TrackList: TrackListScreen,
TrackDetail: TrackDetailScreen
}),
TrackCreate: TrackCreateScreen,
Account: AccountScreen
})
})
const App = createAppContainer(switchNavigator)
export default () => {
return (
<UserProvider value={'hey'}>
<App />
</UserContext.Provider>
)
}
appContext.js
import React, { createContext } from 'react'
const UserContext = React.createContext()
export const UserProvider = UserContext.Provider
export const UserConsumer = UserContext.Consumer
export default UserContext
SignupScreen.js
import React, { useState, useContext, Component } from 'react'
import { View, StyleSheet, Button } from 'react-native'
import { Text, Input } from 'react-native-elements'
import Spacer from '../components/Spacer'
import { signUpUser } from '../functions/functions.js'
import { UserContext } from '../context/appContext.js'
const SignupScreen = ({ navigation, value }) => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<View style={styles.container}>
<Spacer>
<Text h3>Sign up for goodgrowth</Text>
</Spacer>
<Input
label="Email"
value={email}
onChangeText={setEmail}
autoCapitalize="none"
autoCorrect={false}
/>
<Spacer />
<Input
secureTextEntry
label="Password"
value={password}
onChangeText={setPassword}
autoCapitalize="none"
autoCorrect={false}
/>
<Spacer>
<Button
title="Sign up"
onPress={() => signUpUser(email, password)}
/>
<Button
title="Sign in"
onPress={() => navigation.navigate('Signin')}
/>
</Spacer>
</View>
)
}
SignupScreen.navigationOptions = () => {
return {
header: null
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
marginBottom: 250
}
})
export default SignupScreen
TypeError: render is not a function. (In 'render(newValue)', 'render' is an instance of Object).
Why is this not working?

You have exported UserProvider as a named export but you are importing as default in App.js which gives you UserContext and not UserProvider
Also your syntax of UserProvider is incorrect.
Use it like
import { UserProvider } from './src/context/appContext' // named import
...
export default () => {
return (
<UserProvider value={'hey'}>
<App />
</UserProvider>
)
}

Using the curly braces seemed to have done the trick! Thanks Shubham!
I also had to change the following in SignupScreen, so the other way around, from yes-curly-braces to no-curly-braces:"
import { UserContext } from '../context/appContext.js'
TO
import UserContext from '../context/appContext.js'
Because UserContext was the default export there. But it's still a React object, no? Why would it be 'undefined' if I left it in curly braces?

Related

react context in another screen

Good evening everyone,
I created two screens and I would like to pass the context to one of this screen but it doesn't work.
Here what I have so far :
App :
import React from 'react';
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import HomeScreen from "./src/screens/HomeScreen";
import ExpenseScreen from "./src/screens/ExpenseScreen";
import BalanceScreen from "./src/screens/BalanceScreen";
import {ExpenseProvider} from "./src/context/ExpenseContext";
const navigator = createStackNavigator(
{
Home:HomeScreen,
Expense:ExpenseScreen,
Balance: BalanceScreen
},
{
initialRouteName:"Home",
defaultNavigationOptions:{
title:"App",
}
}
)
const App = createAppContainer(navigator);
export default () => {
return <ExpenseProvider>
<App/>
</ExpenseProvider>
};
HomeScreen:
import React from "react";
import { Text, StyleSheet, View, Button} from "react-native";
const HomeScreen = ({navigation}) => {
return(
<View>
<Text style={styles.text}>HomeScreen</Text>
{/*Shows default display*/}
<Button
onPress={()=> navigation.navigate('Expense')}
title="Expense Tracker"
/>
<Button
onPress={()=> navigation.navigate('Balance')}
title="Balance"
/>
</View>
)
};
const styles = StyleSheet.create({
text: {
fontSize: 30
}
});
export default HomeScreen;
Provider :
import React from 'react';
const ExpenseContext = React.createContext();
export const ExpenseProvider = ({children}) => {
const expensePosts = [
{title: 'Expense Post #1'},
{title: 'Expense Post #2'},
{title: 'Expense Post #3'}
]
return <ExpenseContext.Provider value={expensePosts}>{children}</ExpenseContext.Provider>
};
export default ExpenseContext;
Screen :
import React, {useContext} from "react";
import { Text, StyleSheet, View, Button, Flatlist } from "react-native";
import ExpenseContext from '../context/ExpenseContext';
const ExpenseScreen = () => {
const expensePosts = useContext(ExpenseContext)
return(
<View>
<Text style={styles.text}>Expense Tracker</Text>
<Flatlist
data={expensePosts}
keyExtractor={(expensePost) => expensePost.title}
renderItem={({item}) => {
return <Text>{item.title}</Text>
}}
/>
</View>
)
};
const styles = StyleSheet.create({
text: {
fontSize: 30
}
});
export default ExpenseScreen;
I am trying to import the context into the "expenseScreen" but it doesn't work.
The error message I have is " element type is invalid: expected a string or a class but got undefined. You likely forgot to export your component from the file it's defined in or you might have mixed up default and name imports. Check the render method of 'ExpenseScreen'".
What am I missing ?
Does the Expenseprovider exported in the App only applies for the 'homeScreen' ?
Thank you very much in advance and hope this is clear enough.

React Native Context - TypeError: undefined is not an object (evaluating '_useContext.theme')

I keep getting an error when I try to use Context API for dark/light mode in App.js
Theme.js
import React, { useState } from "react";
export const ThemeContext = React.createContext();
//theme = light, dark
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("dark");
const toggleTheme = () => {
if (theme === "light") setTheme("dark");
else setTheme("light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
App.js
import { ThemeContext, ThemeProvider } from "./app/utility/ThemeManager";
export default function App() {
const { theme } = useContext(ThemeContext); // This is throwing the error
return (
<ThemeProvider>
...//Rest of my app
//How I'd like to use my theme
<StatusBar style={theme === "dark" ? "light" : "dark"} />
</ThemProvider>
);
};
I'd like to understand why this is throwing the error and how I could fix it?
Thanks in advance!
Import ThemeProvider in index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "./Theme.js";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<ThemeProvider>
<App />
</ThemeProvider>,
rootElement
);
and in App.js
import {ThemeContext} from "./Theme.js"
import StatusBar from './StatusBar';
import {useContext} from 'react'
export default function App() {
const { theme } = useContext(ThemeContext);
return (
<>
..... Rest Of your Code
</>
);
};

How to change refs from one child component to other child component?

I am working on a chating app with reactjs and i have my components as:
-ChatBox
|-MessageList
|-InputArea
I want to update(re-render,basically append a "li" and scroll down automatically) MessageList component if user provides an input from Input Area.
What i have thought so far is to pass ref from chatbox to MessageList and and then use those refs in InputArea. But i think it is not a good approach. Can someone suggest me what to do.
Update:
My MessageList code:
import React, { useContext,useRef, useState, useEffect } from 'react';
import Message from './Message';
import "./MessageList.css";
import { AuthContext } from '../../Shared/Context/AuthContext';
const MESSAGES=[{convId:1, msgId:1 ,from:'u1',to:'u2',msg:"hello"},
{convId:1, msgId:2 ,from:'u2',to:'u1',msg:"hi"},
{convId:1, msgId:3 ,from:'u1',to:'u2',msg:"how ru"},
{convId:1, msgId:4 ,from:'u2',to:'u1',msg:"goood!!!"},
{convId:1, msgId:5 ,from:'u1',to:'u2',msg:"party ?!"},
{convId:1, msgId:6 ,from:'u2',to:'u1',msg:"yeah!!!"}];
const MessageList=(props)=>{
const msgBoxRef=useRef();
const [messages,setMessages]=useState(MESSAGES);
useEffect(()=>{
msgBoxRef.current.scrollTop=msgBoxRef.current.scrollHeight;
},[messages]);
useEffect(()=>{
if(msgBoxRef.current.scrollTop===msgBoxRef.current.scrollHeight)
msgBoxRef.current.scrollTop=msgBoxRef.current.scrollHeight;
},[])
const auth =useContext(AuthContext);
return (<div className="messages" ref={msgBoxRef}>
<ul>
{messages.map((msg)=>(
<Message key={msg.msgId} className={(msg.from===auth.user.userName)?"sent":"replies"} imageSrc="http://emilcarlsson.se/assets/mikeross.png">{msg.msg}</Message>
))}
</ul>
</div>);
};
export default MessageList;
Input Component code:
import React from 'react';
import {InputGroup,FormControl,Button} from 'react-bootstrap';
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPaperPlane } from "#fortawesome/free-solid-svg-icons";
const Input=(props)=>{
return (<InputGroup className="mb-0">
<FormControl className="input-msg shadow-none" placeholder="Enter new message..." aria-label="new task" />
<InputGroup.Append>
<Button className="send-btn shadow-none">
<FontAwesomeIcon icon={faPaperPlane} />
</Button>
</InputGroup.Append>
</InputGroup>);
};
export default Input;
ChatBox code:
//TODO: IMPLEMENT CHAT CARD
import React, { useState, useRef } from "react";
import { Card } from "react-bootstrap";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faArrowLeft } from "#fortawesome/free-solid-svg-icons";
import MessageList from "./MessageList";
import Input from "./Input";
import Conversations from "./Conversations";
import "./ChatBox.css";
const ChatBox = (props) => {
const [conversation, setConversation] = useState(null);
const [backBtn,setBackBtn]=useState(false);
const selectedConv = (profile) => {
setConversation(profile);
setBackBtn(true);
};
const backBtnHandler=()=>{
setConversation(null);
setBackBtn(false);
}
return (
<Card className="frame">
{conversation ? (
<React.Fragment>
<Card.Header><FontAwesomeIcon icon={faArrowLeft} onClick={backBtnHandler}/>{" "+conversation.name}</Card.Header>
<MessageList chatId={conversation.id}/>
<Input />
</React.Fragment>
) : (
<React.Fragment>
<Card.Header>Chatbox</Card.Header>
<Conversations selectedChat={selectedConv} />
</React.Fragment>
)}
</Card>
);
};
export default ChatBox;

Add function not adding to Redux State

I am going to post my question here and hopefully learn something! I've been following a tutorial and do not yet have the insight into React and Redux to effectively figure out what is going on. I want to create functionality that adds and removes businesses to global state in React and Redux. From what I've Googled I know the file structures vary depending on the project so I will post all of my files here. I have them divided into actions.js, reducers.js, state.js, and store.js. I have an add Listing view with React and would like to add remove functionality to my view listings view. Here are my files:
redux/actions.js:
export const addListing = (newBusiness) => {
return {
type: 'ADD_LISTING',
value: newBusiness
}
}
export const removeListing = (index) => {
return {
type: 'REMOVE_LISTING',
value: index
}
}
redux/reducers.js:
import { combineReducers } from 'redux';
import { addBusiness, removeBusiness } from './actions'
const user = (state = null) => state
// add switch statements in here
const businesses = (state = [], action) => {
switch(action.type) {
case 'ADD_BUSINESS':
return [ ...state, action.value ]
case 'REMOVE_BUSINESS':
const newState = [ ...state ]
newState.splice(action.value, 1);
return newState;
default: // need this for default case
return state
}
}
export default combineReducers({ user, businesses })
redux/state.js
export default {
user: {
username: 'test-user-1',
email: 'test-user#example.com'
},
businesses: [
{
"id": 15,
"name": "My pizza",
"description":"Homemade pizza shop",
"address": "123 Taco Street",
"hours": "9-5"
}
]
};
redux/store.js
import { createStore } from 'redux'
import reducers from './reducers'
import state from './state'
export default createStore(reducers, state)
containers/addListing.js
import { connect } from "react-redux";
import { addListing } from '../redux/actions';
import AddListing from '../components/addListing'
const mapDispatchToProps = (dispatch) => {
return {
addListing: (business) => dispatch(addListing(business)),
}
}
export default connect(null, mapDispatchToProps)(AddListing)
containers/removeListing.js
import { connect } from "react-redux";
import { removeListing } from '../redux/actions';
const mapDispatchToProps = (dispatch) => {
return {
removeCar: (business) => dispatch(removeListing(business)),
}
}
export default connect(null, mapDispatchToProps)(removeListing)
containers/Listing.js:
import { connect } from 'react-redux'
import Listing from '../components/Listing'
const mapStateToProps = (state) => {
return {
businesses: state.businesses,
user: state.user.username
}
}
export default connect(mapStateToProps)(Listing)
components/addListing.js
import React from 'react';
import { InputLabel } from '#material-ui/core'
import { Input } from '#material-ui/core'
import { FormControl } from '#material-ui/core';
import { Button } from '#material-ui/core';
import { TextField } from '#material-ui/core';
import '../redux/state';
class addListing extends React.Component {
state = {
name: '',
description: '',
address: '',
hours: ''
}
handleTextChange = (e) => {
const newState = { ...this.state }
newState[e.target.id] = e.target.value
this.setState(newState)
console.log(this.state)
}
handleSubmit = (e) => {
e.preventDefault()
const payload = { ...this.state }
console.log("THE BUSINESS", payload)
this.props.addListing(payload)
console.log(this.props)
}
componentDidUpdate = (prevProps, prevState) => {
if (prevState.open !== this.state.open) {
this.setState({
name: '',
description: '',
address: '',
hours: ''
})
}
}
render(){
return (
<div className="App">
<form
onSubmit={this.handleSubmit}
style={{ display: 'flex', flexDirection: 'column', width: '350px' }}>
<TextField
id="name"
name="name"
placeholder="Name"
value={this.state.name}
onChange={this.handleTextChange}
required />
<TextField
id="description"
name="description"
placeholder="Description"
value={this.state.description}
onChange={this.handleTextChange}
required />
<TextField
id="address"
name="address"
placeholder="Address"
value={this.state.address}
onChange={this.handleTextChange}
required />
<TextField
id="hours"
name="hours"
placeholder="Hours"
value={this.state.hours}
onChange={this.handleTextChange}
required />
<br />
<Button variant="contained" color="primary" type="submit">Submit</Button>
</form>
</div>
);
}
}
export default addListing;
components/Listing.js:
import React from 'react'
import {
Container,
Table,
TableBody,
TableCell,
TableHead,
TableRow
} from '#material-ui/core'
import DeleteIcon from '#material-ui/icons/Delete'
import addListing from '../containers/addListing'
import removeListing from '../containers/removeListing'
import businesses from '../redux/state'
import user from '../redux/state';
const Listing = (props) => {
console.log(props.businesses)
return (
<Container maxWidth="lg" className="car-container">
<h4>Welcome, {props.user.username}</h4>
<div className="flex-container">
</div>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Name</TableCell>
<TableCell>Description</TableCell>
<TableCell>Address</TableCell>
<TableCell>Hours</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.businesses.map((businesses, idx) => (
<TableRow key={businesses.id}>
<TableCell component="th" scope="row">
</TableCell>
<TableCell>{businesses["name"]}</TableCell>
<TableCell>{businesses["description"]}</TableCell>
<TableCell>{businesses["address"]}</TableCell>
<TableCell>{businesses["hours"]}</TableCell>
<TableCell>
<DeleteIcon
// add onClick method here
// onClick={props.removeCar(idx)}
className="icon text-red"
onClick={ () => this.props.removeListing(idx)}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Container>
)
}
export default Listing
App.js:
import React from 'react';
import Navigation from './components/Navigation'
import './App.css'
import Router from './Router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './redux/store'
function App() {
return (
<Provider store={store}>
<BrowserRouter>
<Navigation />
<Router />
</BrowserRouter>
</Provider>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Login from './components/Login'
import App from './App';
import * as serviceWorker from './serviceWorker';
import Listing from '../src/components/Listing';
import { Provider } from 'react-redux';
ReactDOM.render(
<App />,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
So far I know it's something simple. I am getting no errors but it just doesn't seem to add the new listings to global state. When I get to the listing view it only displays the Business I included as a default in state.js. I will try to reply in a quick manner and please let me know if more info is needed. Thanks!
I see that the names of your actions are different ADD_LISTING vs ADD_BUSINESS, REMOVE_LISTING vs. REMOVE_BUSINESS.
In the addListing you have {type: 'ADD_LISTING', ...}, in your reducer you have case 'ADD_BUSINESS': The strings are different. They need to match. Try renaming ADD_BUSINESS => ADD_LISTING, and REMOVE_BUSINESS=>REMOVE_LISTING.
Regarding the crash on line 50. You don't need this. because your component is not a class type. Change it to onClick={ () => props.removeListing(idx)}.
You're missing removeListing in the mapDispatchToProps.
Also, Redux DevTools plugin for Chrome can help you a lot in debugging issues with redux..

getInputData is undefined

I get this error that getInputData is undefined, please what am I doing wrong?
getInputData simply gets the users inputs....I'm using redux. I defined getInputData in my function called handleInput or is it not well defined......
import React from "react";
import styles from "./style";
import { Text } from "react-native";
import { View, Input, InputGroup } from "native-base";
import Icon from "react-native-vector-icons/FontAwesome";
import { SearchBar } from "react-native-elements";
export const SearchBox = ({ getInputData }) => {
const handleInput = (key, val) => {
getInputData({
key,
value: val
});
};
return (
<View style={styles.searchBox}>
<View style={styles.inputWrapper}>
<Text style={styles.label}>PICK UP</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A" />
<Input
style={styles.inputSearch}
placeholder="Enter Pickup Location"
onChangeText={handleInput.bind(this, "pickUp")}
/>
</InputGroup>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.label}>DROP OFF</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A" />
<Input
style={styles.inputSearch}
placeholder="Enter Drop Off Location"
onChangeText={handleInput.bind(this, "dropOff")}
/>
</InputGroup>
</View>
</View>
);
};
export default SearchBox;
this is my mapContainer.js where inputData is passed down as a prop to SearchBox.
import React from 'react';
import styles from './style';
import {View} from 'native-base';
import MapView from 'react-native-maps';
import SearchBox from '../SearchBox';
import SearchResults from '../SearchResults';
export const MapContainer= ({region, getInputData}) => {
return(
<View style={styles.container}>
<MapView
provider={MapView.PROVIDER_GOOGLE}
style={styles.map}
region={region}
>
<MapView.Marker
coordinate={region}
pinColor="green"/>
</MapView>
<SearchBox getInputData={getInputData}/>
<SearchResults/>
</View>
)
}
export default MapContainer
This is where I connect mapStateToProps to my mapActionCreators
import {connect} from "react-redux";
import {
getCurrentLocation,
getInputData,
} from '../../actions/currentLocation';
import { MapContainer } from '../MapContainer';
import Home from "../../screens/Home";
const mapStateToProps=(state)=>({
region:state.region,
inputData:state.inputData || {}
});
const mapActionCreators = {
getCurrentLocation,
getInputData,
};
export default connect(mapStateToProps,mapActionCreators)(Home);
this is my Home code.
import React from 'react';
import { View, Text } from 'react-native';
import styles from './styles';
import { Container} from 'native-base';
import { MapContainer} from '../../components/MapContainer';
import GetLocation from 'react-native-get-location'
import {Dimensions} from "react-native";
const {width,height}=Dimensions.get("window");
const ASPECT_RATIO=width/height;
const LATITUDE_DELTA=0.922;
const LONGITUDE_DELTA=ASPECT_RATIO*LATITUDE_DELTA
class Home extends React.Component{
constructor(props){
super(props);
this.state={
latitude:3.14662,
longitude:101.6984,
latitudeDelta:LATITUDE_DELTA,
longitudeDelta:LONGITUDE_DELTA
}
}
componentDidMount(){
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
this.setState({
latitude:location.latitude,
longitude:location.longitude
})
console.log(location)
console.log(this.state.longitude);
})
.catch(error => {
const { code, message } = error;
console.warn(code, message);
}) }
render(){
const region={
latitude:this.state.latitude,
longitude:this.state.longitude,
latitudeDelta:this.state.latitudeDelta,
longitudeDelta:this.state.longitudeDelta
}
return(
<Container>
<MapContainer region={region} getInputData={this.props.getInputData} />
</Container>
);
}
}
export default Home;
You should use connect on the Home page like
import React from 'react';
import {connect} from "react-redux";
import { View, Text } from 'react-native';
import styles from './styles';
import { Container} from 'native-base';
import { MapContainer} from '../../components/MapContainer';
import GetLocation from 'react-native-get-location'
import {Dimensions} from "react-native";
import {
getCurrentLocation,
getInputData,
} from '../../actions/currentLocation';
const {width,height}=Dimensions.get("window");
const ASPECT_RATIO=width/height;
const LATITUDE_DELTA=0.922;
const LONGITUDE_DELTA=ASPECT_RATIO*LATITUDE_DELTA
class Home extends React.Component{
constructor(props){
super(props);
this.state={
latitude:3.14662,
longitude:101.6984,
latitudeDelta:LATITUDE_DELTA,
longitudeDelta:LONGITUDE_DELTA
}
}
componentDidMount(){
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
this.setState({
latitude:location.latitude,
longitude:location.longitude
})
console.log(location)
console.log(this.state.longitude);
})
.catch(error => {
const { code, message } = error;
console.warn(code, message);
}) }
render(){
const region={
latitude:this.state.latitude,
longitude:this.state.longitude,
latitudeDelta:this.state.latitudeDelta,
longitudeDelta:this.state.longitudeDelta
}
return(
<Container>
<MapContainer region={region} getInputData={this.props.getInputData} />
</Container>
);
}
}
const mapStateToProps=(state)=>({
region:state.region,
inputData:state.inputData || {}
});
const mapActionCreators = {
getCurrentLocation,
getInputData,
};
export default connect(mapStateToProps,mapActionCreators)(Home);

Resources