Redux thunk with rendering using App class - I am using react-native and redux thunk to call the dispatcher via componentDidMount of App class and receiving errors "props is not defined" and "Unable to find module for EventDispatcher".
Would request for further help on this from experts.
index.js
import React from 'react';
import {AppRegistry} from 'react-native';
import {Provider} from 'react-redux';
import configureStore from './configureStore';
import App from './App';
import {name as appName} from './app.json';
const store = configureStore();
const rnredux = () => (
<Provider store={store}>
<App />
</Provider>
)
AppRegistry.registerComponent(appName, () => rnredux);
app.js
import React from 'react';
import {Platform, TouchableHighlight, StyleSheet, Text, View} from 'react-native';
import {connect} from 'react-redux'
import {fetchPeopleFromAPI} from './actions'
class App extends React.Component {
componentDidMount() {
this.props.getPeople();
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native! & Redux</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<TouchableHighlight onPress={props.getPeople} style={styles.buttonText}>
<Text>Fetch Data</Text>
</TouchableHighlight>
{
isFetching && <Text>Loading</Text>
}
{
people.length? (
people.map((person,index) => {
return (
<View key={index}>
<Text>Name: {person.breedName}</Text>
</View>
)
})
) : null
}
</View>
);
}
}
const {people, isFetching} = props.people
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
buttonText: {
backgroundColor: 'red',
height:60,
justifyContent: 'center',
alignItems: 'center',
}
});
function mapStateToProps (state) {
return {
people: state.people
}
}
function mapDispatchToProps (dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
Remove this part from your code completely:
function mapDispatchToProps (dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
in your export statement amend into:
export default connect(
mapStateToProps,
{ fetchPeopleFromAPI }
)(App)
and finally in your componentDidMount:
componentDidMount() {
this.props.fetchPeopleFromAPI();
}
Related
I'm new to react and mobx.
I'm trying to use mobx to update a simple counter and display the count number.
When I click on the button "Add" I can see in the logs that counterStore.count is increasing but counter shown in the <Text></Text> remains equal to 0.
Can you please me tell me what is wrong?
index.tsx
import { observer } from "mobx-react";
import React from "react";
import { Button, StyleSheet, Text, View } from "react-native";
import CounterStore from './stores/CounterStore';
export function App() {
const counterStore = new CounterStore(0);
return (
<View style={styles.container}>
<View style={styles.wrapper}>
<Text>{counterStore.count}</Text>
<Button
title="Add"
onPress={() => {
counterStore.addToCount();
console.log("count = ", counterStore.count);
}}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
height: "100%"
},
wrapper: {
backgroundColor: "#F5FCFF",
width: "100%",
maxWidth: 425
}
});
export default observer(App);
CounterStore.ts
import { action, makeObservable, observable } from "mobx";
class CounterStore {
#observable count: number;
constructor(count: number){
this.count = count
makeObservable(this);
}
#action
addToCount(){
this.count++;
}
}
export default CounterStore;
Output & Logs
output
I tried to improve my code based on Jay Vaghasiya's answer, but still got the same behaviour.
CounterStore.ts
import { action, makeObservable, observable } from "mobx";
import React from "react";
class CounterStore {
count = 0;
constructor(){
makeObservable(this), {count: observable};
}
#action
addToCount(){
this.count++;
}
}
export const CounterStoreContext = React.createContext(new CounterStore());
index.tsx
import { observer } from "mobx-react";
import React from "react";
import { Button, StyleSheet, Text, View } from "react-native";
import { CounterStoreContext } from './stores/CounterStore';
const App = observer(() => {
const counterStore = React.useContext(CounterStoreContext);
return (
<View style={styles.container}>
<Text>{counterStore.count}</Text>
<Button
title="Add"
onPress={() => {
counterStore.addToCount();
console.log("count = ", counterStore.count);
}}
/>
</View>
);
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
height: "100%",
},
wrapper: {
backgroundColor: "#F5FCFF",
width: "100%",
maxWidth: 425
}
});
export default App;
I have a Functional style React Native component where I can happily call my Redux Action:
import * as React from 'react';
import {Text, View, StyleSheet, Dimensions} from 'react-native';
import MultiSlider from '#ptomasroos/react-native-multi-slider';
import {useSelector, useDispatch} from 'react-redux';
import {updateNumPosInCode} from './redux/actions/gameplayAction';
const width = Dimensions.get('window').width;
export default function Settings() {
const dispatch = useDispatch();
const numPosInCode = useSelector(store => store.gameplay.numPosInCode);
const handleNumPosInCodeUpdate = num => {
console.log(`handleNumPosInCodeUpdate: ${num}`);
dispatch(updateNumPosInCode(num));
};
return (
<View style={[styles.container]}>
<View>
<Text style={styles.text}>Number of Positions in the Code</Text>
<MultiSlider
sliderLength={width - 40}
values={[6]}
optionsArray={[4, 5, 6]}
snapped={true}
onValuesChangeFinish={handleNumPosInCodeUpdate.bind(this)}
/>
<Text style={styles.text}>{numPosInCode}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
flex: 1,
padding: 20,
justifyContent: 'space-evenly',
},
text: {
textAlignVertical: 'center',
textAlign: 'center',
},
});
However when I try to call the same action from this Class style React Native component, nothing happens. Can you spot my mistakes?
import * as React from 'react';
import {View, StyleSheet, Button} from 'react-native';
import {connect} from 'react-redux';
import {updateNumPosInCode} from './redux/actions/gameplayAction';
class ColourBoard extends React.Component {
constructor(props) {
super(props);
this.state = {
numPosInCode: 5,
};
}
render() {
return (
<View
style={[
styles.MainContainer,
{
top: this.state.top,
},
]}>
<Button
onClick={this.props.updateNumPosInCode(4)}
title="Test"
color="#841584"
/>
</View>
);
}
}
const mapStateToProps = state => ({
numPosInCode: state.numPosInCode,
});
// Actions:
const mapDispatchToProps = () => ({
updateNumPosInCode,
});
const styles = StyleSheet.create({
MainContainer: {
left: 1,
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
position: 'absolute',
zIndex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps())(ColourBoard);
Yes. Your mapDispatchToProps usage is wrong.
With connect, there's two ways to use mapDispatch:
passing in an object full of action creators, in which case you get all the same functions as props but already wrapped up to dispatch actions when called
passing in a function that gets dispatch as an argument, in which case it's up to you to do the wrapping
In this case, you're passing in the function form of mapDispatch... **but you are only returning the plain action creators as the props, and not wrapping them to make use of dispatch!
The simplest answer here is to change it to const mapDispatch = {updateNumPosInCode} and pass that instead.
The better answer is to switch this to be a function component and make use of the useDispatch hook instead.
I have been trying to use mobX to apply on React Native Functional Component.
So I use these 2 libraries - mobx & mobx-react-lite.
I made a simple counter app and I also useContext hook along with this.
After increasing the value, it doesn't apply on the screen. However, it appeared on my console.
The change got displayed on after I had refreshed my code by saving it (I didn't change the code)
How do I solve this issue?
App.js
import { StyleSheet, Text, View,Button } from 'react-native';
import { CounterStoreContext } from './components/CounterStore';
import { observer } from "mobx-react-lite";
import React, { useContext, useState } from "react";
const App = observer(() => {
// const [count, setCount] = useState(0);
const counterStore = useContext(CounterStoreContext)
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome</Text>
<Text style={styles.text}>Just Press the damn button</Text>
{/* <Text style={styles.text}>{count}</Text> */}
{/* <Button title="Increase" onPress={()=>{setCount(count+1)}}/> */}
<Text style={styles.text}>{counterStore.count}</Text>
<Button title="Increase" onPress={()=>{
counterStore.count++;
console.log(counterStore.count)
// setCount(counterStore.count)
}}/>
</View>
);
})
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
welcome:{
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
},
text:{
fontSize: 14,
textAlign: 'center',
},
});
export default App;
CounterStore.js
import { observable, observe } from "mobx";
import { createContext } from "react";
class CounterStore {
#observable count = 0;
}
export const CounterStoreContext = createContext(new CounterStore())
Since MobX 6 the #observable decorator is not enough. You need to use makeObservable / makeAutoObservable in the constructor as well.
class CounterStore {
#observable count = 0;
constructor() {
makeAutoObservable(this);
}
}
I have a component which I'm trying to connect with the global redux store. But when I do, I got an error which says that "Could not find "store" in the context of "Connect(App)"".
Here is my code:
App.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import {connect} from 'react-redux'
function App() {
const geo = navigator.geolocation
if(!geo) {
} else {
geo.getCurrentPosition((response)=>{console.log(response)}, (response) => {console.log(response)})
}
return (
<View style={{padding: 40}}>
<View style={styles.form}>
<TextInput placeholder="Example text" style={styles.cityInput}/>
<Button title="Press ME!" onPress={(e)=> {console.log(e)}}/>
</View>
</View>
);
}
const styles = StyleSheet.create({
form: {
flexDirection: 'row',
justifyContent: 'space-between',
maxWidth: '100%'
},
cityInput: {
borderColor: 'black',
borderWidth: 2,
borderRadius: 5,
padding: 5,
width: '80%'
}
});
export default connect()(App)
index.js
import React from 'react'
import {AppRegistry} from 'react-native';
import App from './App'
import { name as appName } from './app.json'
import {Provider} from 'react-redux'
import configureStore from './src/reducers/store'
const store = configureStore();
const SunApp = () =>
<Provider store={store}>
<App />
</Provider>
AppRegistry.registerComponent(appName, () => SunApp)
store.js
import positionReducer from './positionReducer';
import sunReducer from './sunReducer';
import cityReducer from './cityReducer';
import {createStore, combineReducers} from 'redux';
const reducers = combineReducers({
positionReducer,
sunReducer,
cityReducer,
})
const configureStore = () => createStore(
reducers,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
export default configureStore;
Here's the error
I can't figure out what I have to do to get rid of this error, so any feedback will be much appreciated
That error seems to come from your react native web entry point where the App is not wrapped in a <Provider (visible in the error message of the screenshot), not from the entry point you shared here.
So, indeed, the problem was in the entry point of my App. Because I was using managed workflow, the entry point of my application was registered to the App component, and my index.js file didn't make anything. So I managed to change my project structure a little bit so now my files looks like this:
App.js
import React from 'react';
import Main from './src/components/Main'
import {Provider} from 'react-redux'
import configureStore from './src/reducers/store'
const store = configureStore();
function App() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
export default App
Main.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
import {useEffect} from 'react'
import {connect, useSelector, useDispatch} from 'react-redux'
import axios from 'axios'
import {setPosition, setSun, setCity, setUndef, setInput} from '../actions'
function Main() {
const position = useSelector(state => state.positionReducer)
const sun = useSelector(state => state.sunReducer)
const city = useSelector(state => state.cityReducer)
const input = useSelector(state => state.inputReducer)
const dispatch = useDispatch();
function fail(error) {
alert(`Currently we can't get your location because: "${error.message}"`)
}
function success(response) {
dispatch(setPosition(response.coords));
}
function findCity(e) {
e.preventDefault()
dispatch(setCity(input))
axios.get('https://api.openweathermap.org/geo/1.0/direct', {
params: {
q: input,
limit: 1,
appid: 'a913b85241698a00b1014abe62a5ca0e'
}
})
.then(response => {
console.log(response);
if(response.data[0]) {
dispatch(setPosition(response.data[0]))
} else {
dispatch(setUndef())
}
})
}
useEffect(() => {
const geo = navigator.geolocation;
if(!geo) {
fail()
} else {
geo.getCurrentPosition(success, fail)
}
// eslint-disable-next-line
},[])
useEffect(() => {
if(position.lat && position.lat !== 'Not Found') {
axios.get('https://api.openweathermap.org/data/2.5/weather', {
params: {
lat: position.lat,
lon: position.lon,
appid: 'a913b85241698a00b1014abe62a5ca0e'
}
})
.then((response) => {
const sunriseTime = new Date(response.data.sys.sunrise * 1000)
const sunsetTime = new Date(response.data.sys.sunset * 1000)
const sun = {
sunrise: `${sunriseTime.getHours()}:${sunriseTime.getMinutes()}`,
sunset: `${sunsetTime.getHours()}:${sunsetTime.getMinutes()}`
}
dispatch(setSun(sun))
})
axios.get('https://api.openweathermap.org/geo/1.0/reverse', {
params: {
lat: position.lat,
lon: position.lon,
limit: 1,
appid: 'a913b85241698a00b1014abe62a5ca0e'
}
})
.then((response) => {
dispatch(setCity(response.data[0].name))
})
}
// eslint-disable-next-line
},[position])
return (
<View style={{padding: 40}}>
<View style={styles.form}>
<TextInput
placeholder="Example text"
style={styles.cityInput}
onChangeText={(e) => {dispatch(setInput(e))}}
onSubmitEditing={(e) => {dispatch(setInput(e.nativeEvent.text)); findCity(e)}}
/>
<Button title="Press ME!" onPress={findCity}/>
</View>
<View style={styles.info}>
<Text style={styles.infoText}>City: {city}</Text>
<Text style={styles.infoText}>Longitude :{position.lon}</Text>
<Text style={styles.infoText}>Latitude: {position.lat}</Text>
<Text style={styles.infoText}>Sunrise time: {sun.sunrise}</Text>
<Text style={styles.infoText}>Sunset time: {sun.sunset}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
form: {
flexDirection: 'row',
justifyContent: 'space-between',
maxWidth: '100%'
},
cityInput: {
borderColor: 'black',
borderWidth: 2,
borderRadius: 5,
padding: 5,
width: '80%'
},
info: {
marginTop: 0,
marginBottom: 0,
marginLeft: 'auto',
marginRight: 'auto',
width: 'max-content'
},
infoText: {
fontSize: 20,
fontWeight: 700
}
});
export default connect()(Main)
I'm building a React Native App with expo and I have an image I want to display on my login screen that should cover the whole image but for some reason it's not loading and I have a blank screen. My terminal says "Could not find image file:///Users/BlahBlah/Library/Developer/CoreSimulator/Devices/3FE078A1-2C0A-4308-B256-BFBF1B246A85/data/Containers/Bundle/Application/08978CAA-74FC-476D-939C-9CBDF1E4B9D9/Exponent-2.13.0.app/./assets/Images/TemplatePic.jpg
- node_modules/react-native/Libraries/BatchedBridge/NativeModules.js:104:55 in
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:414:4 in __invokeCallback
- ... 4 more stack frames from framework internals"
LoginScreen
import React from 'react';
import { View, Image, Dimensions, SafeAreaView, StyleSheet, Text } from 'react-native';
import { AppLoading } from 'expo';
import { Asset } from 'expo-asset';
import { Tab, Tabs, Header } from 'native-base';
import { commonStyles } from './styles/styles';
import SignInScreen from './SignInScreen';
import SignUp from './SignUp';
const { width, height } = Dimensions.get('window');
class LoginScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
isReady: false
};
}
render() {
return (
<View style={styles.background}>
<View style={StyleSheet.absoluteFill}>
<Image
source={('../assets/Image/TemplatePic.jpg')}
style={{ flex: 1, height: null, width: null }}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
background: {
flex: 1,
backgroundColor: '#FFFFFF',
justifyContent: 'center',
alignItems: 'center',
}
});
export default LoginScreen;
App.js
import React from 'react';
import { View, Image, Dimensions, SafeAreaView, StyleSheet, Text } from 'react-native';
import { AppLoading } from 'expo';
import { Asset } from 'expo-asset';
import * as firebase from 'firebase';
import { firebaseConfig } from './config.js';
import { Provider, connect } from 'react-redux';
import RootStack from './RootStack';
import LoginScreen from './App/screens/LoginScreen';
import configureStore from './App/reducers/configureStore';
firebase.initializeApp(firebaseConfig);
// create store from redux
const store = configureStore();
function cacheImages(images) {
return images.map(image => {
if (typeof image === 'string') {
return Image.prefetch(image);
}
return Asset.fromModule(image).downloadAsync();
});
}
const { width, height } = Dimensions.get('window');
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isReady: false
};
}
async _loadAssetsAsync() {
const imageAssets = cacheImages([
('./assets/Images/TemplatePic.jpg'),
]);
await Promise.all([...imageAssets]);
}
render() {
//If the state is not ready then display the apploading oterwise display the app
if (!this.state.isReady) {
return (
<AppLoading
startAsync={this._loadAssetsAsync}
onFinish={() => this.setState({ isReady: true })}
onError={console.warn}
/>
);
}
return (
<View style={styles.background}>
<LoginScreen />
</View>
);
}
}
const styles = StyleSheet.create({
background: {
flex: 1,
backgroundColor: '#FFFFFF',
justifyContent: 'center',
alignItems: 'center',
fontSize: 16
},
textStyle: {
}
});
Try this:
<Image
source={require('../assets/Image/TemplatePic.jpg')}
style={{ flex: 1 }}
resizeMode="cover"
/>