My code is working but I don't understand how "library" and library.id works in the keyExtractor. How library.id get id of the items from "libraries" reducer?
And also "library" in renderItem(library) and "library" in keyExtractor are same?
I would appreciate if anybody can shortly explain this.
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import ListItem from './ListItem';
class LibraryList extends Component {
renderItem(library) {
return <ListItem library={library} />;
}
render() {
return (
<FlatList
data={this.props.libraries}
renderItem={this.renderItem}
keyExtractor={library => library.id}
/>
);
}
}
const mapStateToProps = state => {
return { libraries: state.libraries };
};
export default connect(mapStateToProps)(LibraryList);
library (can be named anything) in your renderItem is coming from your
data={this.props.libraries}
renderItem(library) {
return <ListItem library={library} />;
}
this.props.libraries is coming from redux
- the key name `libraries` can be named anything other than `libraries`
- state.libraries is coming from your redux reducer (check your root reducer)
const mapStateToProps = state => {
return { libraries: state.libraries };
};
this is extracting the key id from your data which is coming from data={this.props.libraries}
keyExtractor={library => library.id}
Related
Hey struggling with this one for a day now.
I am trying to store game data just the gameId and the Level for example Game 1 Level 12
Here is my screen
import React, { Component } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { Text, StyleSheet, Button, View, ImageBackground, Pressable } from 'react- native';
import bg from "../assets/images/1.jpg";
import styles from '../assets/style';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const setScore = async (gameId, level) => {
//// SETS THE SCORE
try {
await AsyncStorage.setItem(scoreKey, level);
console.log(value)
} catch (error) {
console.log(error)
}
};
const getScore = async (gameId) => {
try {
let value = await AsyncStorage.getItem(JSON.stringify(gameId))
if(value !== null) {
// value previously stored
return JSON.stringify(value)
} else {
return "not started"
}
} catch(e) {
// error reading value
}
};
/// This would add game 1 and level 12
setScore('1','12') /// This part works
const theLevel = getScore(1)
export default function Home({navigation, route}) {
return (
<ImageBackground source={bg} resizeMode="cover" style={styles.imageBG}>
<View style={styles.GameList}>
<Text style={styles.mainTitle}>Current Level is {theLevel}</Text>
</View>
</ImageBackground>
);
}
At the bottom of the above code I want to display the level but I get the error
Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead.
However If I alert(theLevel) it works fine can someone tell me what I am doing wrong please
Call getScore function from within useEffect hook of your Home component.
export default function Home({ navigation, route }) {
const [level, setLevel] = useState(0);
useEffect(() => {
async function getMyLevel() {
const lvl = await getScore(1);
setLevel(lvl);
}
getMyLevel();
}, []);
const onPress = async () => {
await setScore('1','12');
};
return (
<ImageBackground source={bg} resizeMode="cover" style={styles.imageBG}>
<View style={styles.GameList}>
<Text style={styles.mainTitle}>Current Level is {level}</Text>
</View>
<Button title="Set Score" onPress={onPress} />
</ImageBackground>
);
}
I have a Node component that is rendering its children using the same Node function. For some reason, the mapStateToProps method is only called for the first node, but not for any of its children.
import * as React from "react";
import { View } from "react-native";
import { connect } from "react-redux";
function Node({ node, nodeId }) {
return (
<View>
{node.children.map((id) => (<Node key={id} nodeId={id} />))}
</View>
);
}
const mapStateToProps = (state, ownProps) => {
// This log is not called for children
console.log(ownProps.nodeId, "Mapping state to props");
return {
node: state.nodes[ownProps.nodeId],
};
};
export default connect(mapStateToProps)(Node);
The only way I can get it to work is by creating a NodeB component in another file (won't work if it's not an import from another file) that renders a Node. Like so:
export function NodeB(props) {
return <Node {...props} />;
}
The problem with that is that I get a Require cycle warning because NodeB import Node and Node imports NodeB....
Any idea why this is happening?
That's because children Node components are not connected to redux store by default. To fix that try the following:
import * as React from "react";
import { View } from "react-native";
import { connect } from "react-redux";
function Node({ node, nodeId }) {
return (
<View>
{node.children.map((id) => <ConnectedNode key={id} nodeId={id} />)}
</View>
);
}
const mapStateToProps = (state, ownProps) => { ... };
const ConnectedNode = connect(mapStateToProps)(Node);
export default ConnectedNode;
I am creating a slide bar, In that, I have used the react-redux library. When I call the class which contains the redux-code, it works fine. I want to show this slide bar after login. Therefore, with conditions (I set a state variable if user login successfully then only this page should get rendered), I tried to call the same file which shows a blank page. I printed the console log. I am able to print all the logs. But with conditions, I am not able to load the data.
I don't know much about react-redux.Can you assist me to resolve this?
My code is,
main.js,
import React, {Component} from 'react';
import {
StyleSheet,
Dimensions,
Platform,
View,
StatusBar,
DrawerLayoutAndroid,
} from 'react-native';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from '../Redux/reducers';
import { setNavigator, setActiveRoute } from "../Redux/actions";
import DrawerContent from '../Navigation/DrawerContent';
import Toolbar from '../Navigation/Toolbar';
import AppNavigation from '../Navigation/AppNavigation';
import { bgStatusBar, bgDrawer } from '../global.styles';
let store = createStore(reducer);
/* getDrawerWidth Default drawer width is screen width - header width
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const getDrawerWidth = () => Dimensions.get('window').width - (Platform.OS === 'android' ? 56 : 64);
export default class Main extends Component {
constructor() {
super();
this.drawer = React.createRef();
this.navigator = React.createRef();
}
componentDidMount() {
store.dispatch(setNavigator(this.navigator.current));
}
openDrawer = () => {
this.drawer.current.openDrawer();
};
closeDrawer = () => {
this.drawer.current.closeDrawer();
};
getActiveRouteName = navigationState => {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
};
render() {
return (
<Provider store={store}>
<DrawerLayoutAndroid
drawerWidth={getDrawerWidth()}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={
() => <DrawerContent closeDrawer={this.closeDrawer} />
}
ref={this.drawer}
>
<View style={styles.container}>
<StatusBar
translucent
animated
/>
<Toolbar showMenu={this.openDrawer} />
<AppNavigation
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = this.getActiveRouteName(currentState);
store.dispatch(setActiveRoute(currentScreen));
}}
ref={this.navigator}
/>
</View>
</DrawerLayoutAndroid>
</Provider>
);
}
}
Login.js
import Main from './main';
render() {
return (
<View>
{this.state.isLoggedIn ?
<Main/>
:
<ChangePassword isUpdatePassword={this.state.isUpdatePassword} callLogin={this.callLogin}/>
);
}
}
If I just call Main class inside render method it works. But It does not work with conditions.
I have been using React Native for a few years and have only recently needed to utilise Redux on a new, more complex project. I am currently in the process of following a number of tutorials trying to work my way through the basics.
I am currently stuck with the following error:
Invariant Vilation: Could not find "store" in either the context of props of "Connect(App)"
I have found a number of posts with information about this error but because of the low amount of knowledge I currently have, I am unsure as to how to correctly implement a fix.
This project was created with create-react-native-app and I am using the Expo app to test.
In my eyes this should work because the root element of App is a Provider element passing the store as a prop which seems to contradict what the error is saying.
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import app from './reducers';
import thunk from 'redux-thunk';
export default function configureStore() {
return createStore(app, applyMiddleware(thunk));
}
App.js:
import React from 'react';
import { Text } from 'react-native';
import { Provider, connect } from 'react-redux';
import configureStore from './configureStore';
import fetchPeopleFromAPI from './actions';
const store = configureStore();
export class App extends React.Component {
componentDidMount() {
props.getPeople()
}
render() {
const { people, isFetching } = props.people;
return (
<Provider store={store}>
<Text>Hello</Text>
</Provider>
);
}
}
function mapStateToProps(state) {
return {
people: state.people
}
}
function mapDispatchToProps(dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
You are trying to access the store in the App component even before it has been passed. Therefore it is not able to find the store.
You need to make a separate component and connect that using react-redux such as
<Provider store={store}>
<ConnectedComponent />
</Provider>
...
class ConnectedComponent extends React.Component {
componentDidMount () {
this.props.getPeople()
}
render() {
return (
<View>
<Text> ... </Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
people: state.people
}
}
function mapDispatchToProps(dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ConnectedComponent);
In my react native app I'm using redux to handle state transition of a Post object -- the state is changed by couple of child components. The Post object has properties like title, name, description which the user can edit and Save.
In the reducer Im using React.addons.update return new state object.
The main container view has 2 custom child components (wrapped in TabBarNavigator).
One of the child component has few TextInputs which is updating a state.
Using the logger middleware and console.log() I see the new state value in the parent view's render() (via this.props.name) but not in the child view.
I'm trying to figure out why the updated state is not propagated to the child container. Any suggestion is much appreciated.
Im at a point where Im thinking of subscribeing to the redux store manually in the child container but it feels wrong
my code looks like this:
MainView
Reducer
configure store etc
The MainView
const React = require('react-native');
const {
Component,
} = React;
const styles = require('./../Styles');
const MenuView = require('./MenuView');
import Drawer from 'react-native-drawer';
import TabBarNavigator from 'react-native-tabbar-navigator';
import BackButton from '../components/BackButton';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as PostActions from '../actions/Actions';
import {Details} from './Article/Details';
import {ArticleSecondary} from './Article/Secondary';
var update = require('react-addons-update');
import configureStore from '../store/configureStore';
class ArticleMainView extends Component {
constructor(props){
super(props);
//var store = configureStore(props.route.post);
this.state = {
};
}
componentDidMount(){
}
savePost() {
console.log(this.props.post.data);
this.props.navigator.pop();
}
render(){
console.log("ArticleMainView: render(): " + this.props.name);
return(
<TabBarNavigator
ref="navComponent"
navTintColor='#346293'
navBarTintColor='#94c1e8'
tabTintColor='#101820'
tabBarTintColor='#4090db'
onChange={(index)=>console.log(`selected index ${index}`)}>
<TabBarNavigator.Item title='ARTICLE' defaultTab>
<Details ref="articleDetail"
backButtonEvent={ () => {
this.props.navigator.pop();
}}
saveButtonEvent={ () => {
this.savePost();
}}
{...this.props}
/>
</TabBarNavigator.Item>
<TabBarNavigator.Item title='Secondary'>
<ArticleSecondary ref="articleSecondary"
{...this.props}
backButtonEvent={ () => {
this.props.navigator.pop();
}}
saveButtonEvent={ () => {
this.savePost();
}}
/>
</TabBarNavigator.Item>
</TabBarNavigator>
);
}
}
function mapStateToProps(state) {
return {
post: state,
text: state.data.text,
name: state.data.name,
description: state.data.description
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(PostActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(ArticleMainView);
The Reducer:
import {Constants} from '../api/Constants';
var update = require('react-addons-update');
export default function postReducer(state, action) {
switch(action.type) {
case Constants.SET_POST_TEXT:
if( state.data.text){
return update(state, {
data: { $merge: {text: action.text }}
});
}else{
return update(state, {
data: { $merge: {text: action.text }}
});
}
break;
case Constants.SET_POST_NAME:
return update(state, {
data: { name: { $set: action.text }}
});
return newO;
break;
case Constants.SET_POST_DESCRIPTION:
return update(state, {
data: { description: { $set: action.text }}
});
break;
default:
return state;
}
}
render scene of the app:
renderScene(route, navigator) {
switch (route.id) {
case "ArticleMainView":
let store = configureStore(route.post);
delete route.post; // TODO: not sure if I should remove this
return (
<Provider store={store}>
<ArticleMainView navigator={navigator} {...route}/>
</Provider>
);
default:
return <LandingView navigator={navigator} route={route}/>
}
}
configureStore:
import { createStore,applyMiddleware,compose } from 'redux'
import postReducer from '../reducers/SocialPostReducer';
import createLogger from 'redux-logger';
const logger = createLogger();
export default function configureStore(initialState){
return createStore(
postReducer,
initialState,
compose(applyMiddleware(logger))
);
}
If anyone stumbles on this question this is how I solved it. In each of the child components I declared a contextTypes object like so
ChildComponentView.contextTypes = {
store: React.PropTypes.object
}
to access the current state in the child component
let {store} = this.context;
store.getState();
I don’t know React Native well but something that threw me off is that you’re effectively creating a store on every render:
case "ArticleMainView":
let store = configureStore(route.post);
delete route.post; // TODO: not sure if I should remove this
return (
<Provider store={store}>
<ArticleMainView navigator={navigator} {...route}/>
</Provider>
);
Store should only be created once per application lifetime. It never makes sense to create it inside render() or renderScene() or similar methods. Please check the official Redux examples to see how the store is typically created.
Another problem is that you don’t show how you update the data, which child component doesn’t get updated, when you expect it to get updated, and so on. This is a lot of code, and it is very hard to help because it is incomplete, and most of it is not relevant to the problem. I would suggest you to remove all the irrelevant code until you can reproduce the problem with a minimal possible complete example. Then you can amend your question to include that example.