I get the following error using redux in native react
Invariant Violation: Invariant Violation: Could not find "store" in
the context of "Connect(Lista)". Either wrap the root component in a
, or pass a custom React context provider to and
the corresponding React context consumer to Connect(Lista) in connect
options.
My code is the following
SettingStore
import {createStore, applyMiddleware} from 'redux';
import Reducers from './Reducer'
import thunk from 'redux-thunk'
export default SettingStore = () => {
let store = createStore(Reducers, applyMiddleware(thunk))
return store
}
my index reducer
import {combineReducers} from 'redux';
import loginreducer from './login.reducer'
export default combineReducers({
login: loginreducer,
})
my index action
import {FETCHING_GETDATA_PARVU} from '../Constante'
import {GetParv} from '../Api/Parvularia.api'
export const getParvuSuccess = (data) => {
return {type: FETCHING_GETDATA_PARVU, data}
}
export const GetParvu = () => {
return (dispatch) => {
dispatch(getData())
GetParv(1)
.then(([response, json]) => {
dispatch(getParvuSuccess(json))
})
.catch((error) => console.log(error))
}
}
This is the maintab.reducer of my reducer
import {FETCHING_GETDATA_PARVU} from '../Constante'
const initialState = {
data: [],
isFeching: false,
error: false
}
export default dataReducer = (state = initialState, action) => {
switch(action.type) {
case FETCHING_GETDATA_PARVU:
return {
...state,
data: action.data,
isFeching: false
}
default:
return state
}
}
and let's say this is my app.js of the redux structure
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import {Provider} from 'react-redux';
import Lista from '../screens/Educadora/Lista';
import SettingStore from './SettingStore'
let store = SettingStore()
const Ap = () => (
<Provider store={store}>
<Lista/>
</Provider>
)
export default Ap;
This last part is the one that generates me more doubt, I think that this is my error but I do not know why I am new in react native
Edit
This is where I want to show the query made with redux
import { connect } from 'react-redux'
import {GetParvu} from '../../Redux/Actions'
class Lista extends React.Component {
componentWillMount() {
this.props.GetParvu()
render(){
return(algoaca)
}
}
}
const mapStateToProps = state => {
return {
parvu: state.data
}
}
const mapDispatchToProps = dispatch => {
return {
GetParvu: () => {
return dispatch(GetParvu(1))
}
}
}
AppRegistry.registerComponent('EqualsMobile', () => Lista);
export default connect(mapStateToProps, mapDispatchToProps)(Lista);
You have connecting your component in wrong way. connect() is high order component that take mapStateToProps as an argument to map store to your component.
You can use it in your component like this.
function mapStateToProps(state){
return {state}
}
export default connect(mapStateToProps)(your_component_name);
Related
I'm using redux sauce. When I dispatch an action, it is giving me "_HelpRedux.default.request is not a function" error. Any help is appreciated. How can I make it right? I'm out of ideas. Thanks in advance
HelpRedux.js
import { createReducer, createActions } from 'reduxsauce';
import Immutable from 'seamless-immutable';
const { Types, Creators } = createActions({
helpPostt: null,
dashboardRequest: ['data'],
})
export const HelpTypes = Types
export default Creators
export const INITIAL_STATE = Immutable({
fetching: false,
data: null,
})
export const helpRequestPost = (state) => {
return state;
}
export const request = (state) => state.merge({ fetching: true });
export const reducer = createReducer(INITIAL_STATE, {
[Types.HELP_POSTT]: helpRequestPost,
[Types.DASHBOARD_REQUEST]: request,
})
HelpScreen.js
import React, { Component } from 'react'
import { View, Text, KeyboardAvoidingView, ScrollView, TouchableOpacity, TextInput, Linking, Platform } from 'react-native'
import { connect } from 'react-redux';
import HelpRedux from '../Redux/HelpRedux';
class HelpScreen extends Component {
tryLogin = () => {
this.props.request(); //_HelpRedux.default.request is not a function
//this.props.helpRequestPost(); //_HelpRedux.default.helpRequestPost is not a function
}
render () {
console.log("help", this.props);
return (
<View>
<TouchableOpacity onPress={this.tryLogin}>
<Text>abc</Text>
</TouchableOpacity>
</View>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
request: () => dispatch(HelpRedux.request()),
helpRequestPost: (key) => dispatch(HelpRedux.helpRequestPost(key)),
}
}
export default connect(null, mapDispatchToProps)(HelpScreen)
. .
OK I made a mistake here. Instead of calling an action, I was calling reducer.
request: () => dispatch(HelpRedux.helpPostt()),
I cannot get the store reducer initialState, so I cannot map to props in the component as well.
Here is my reducer:
const initialState = { currentUser: null }
export default function UserReducer(state = initialState, action){
let nextState;
switch(action.type){
case "USER_CONNECTED":
nextState = {
...state,
currentUser : action.value
}
return nextState;
case "USER_DECONNECTED":
nextState = {
...state,
currentUser : null
}
return nextState;
default:
return state;
}
}
Here is the class that configures the store:
import { createStore, combineReducers } from 'redux';
import UserReducer from './reducers/userReducer'
const rootReducer = combineReducers({
currentUser : UserReducer
});
const configureStore = () => {
return createStore(rootReducer);
}
export default configureStore;
And here is where I initialise the store and pass it to the App thanks to a provider:
import {AppRegistry} from 'react-native';
import React from 'react';
import App from './App';
import {name as appName} from './app.json';
import { Provider } from 'react-redux';
import configureStore from './store/store';
const Store = configureStore();
console.log("STORE :"+ JSON.stringify(Store.getState()));
const RNRedux = () => (
<Provider store = { Store }>
<App />
</Provider>
)
AppRegistry.registerComponent(appName, () => RNRedux);
When I print the "STORE" above, it gives me the right output { currentUser : ...}. Then I connect App.js to the store as follows:
const AppNavigator = createStackNavigator(
{
NewAccount: NewAccountScreen,
Login: LoginScreen
},
{
initialRouteName: "Login"
}
);
const AppContainer = createAppContainer(AppNavigator);
export class App extends React.Component {
constructor(props, context){
super(props, context);
}
render() {
console.log("APP.JS : "+ JSON.stringify(this.props));
return (
<AppContainer />
)
}
}
export default connect()(App);
So at the last line I connect the entire App state to the component props but it gives me {}.
You are missing mapStateToProps param in your connect call.
export default connect()(App);
You need to specify mapping function that will take parts of state and pass it to components props.
To map entire state to props try this:
export default connect(state=>state)(App)
Better practice would be to pass only parts of state that the component needs. That way you would avoid unnecessary re-renders when some other part of state changes. For example if your connected component only needs user first name you could do this:
export default connect(state=>{firstName:state.currentUser.firstName})(App)
I am using Redux with a React Native app, and am getting a red box error when trying to integrate Redux-Persist. As soon as I navigate to the initial screen which has a ProfileForm and has the this.props.age piece of state, I see this:
TypeError: Cannot read property "age" of undefined
I am not quite sure where I am going wrong. I have set initial value of age = 0 in the reducer. So how is it undefined? Here is code that would be relevant:
reducers/UserReducer.js
import { CALCULATE_BMI, CALCULATE_AGE } from '../actions/types';
const INITIAL_STATE = { bmi: 0, age: 0 };
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CALCULATE_BMI:
return { ...state, bmi: action.bmi };
case CALCULATE_AGE:
return { ...state, age: action.age };
default:
return state;
}
};
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import userReducer from './UserReducer';
export default combineReducers({
user: userReducer,
form: formReducer,
});
store/index.js
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducers from '../reducers';
const config = {
key: 'root',
storage,
};
const reducer = persistCombineReducers(config, { reducers });
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(reducer, {}, composeEnhancers(applyMiddleware(thunk, logger)));
const persistor = persistStore(store);
return { persistor, store };
};
ProfileForm.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View, TouchableOpacity, Alert } from 'react-
native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, Field } from 'redux-form';
import * as actions from '../actions';
import DatePickerModal from '../components/DatePickerModal';
class ProfileForm extends Component {
// other code here
<Field name="birthdate" component={DatePickerModal} />
{this.props.age === 0 ? null : (
<Text style={styles.labelStyle}>{`Age: ${this.props.age}`}</Text>
)}
//other code here
}
const mapStateToProps = state => ({
age: state.user.age,
});
export default compose(connect(mapStateToProps, actions), reduxForm({ form: 'Profile', validate }))(
ProfileForm,
);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/es/integration/react';
import MainNavigator from './routes';
import configureStore from './store';
const { persistor, store } = configureStore();
export default class App extends Component {
// other code here
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<MainNavigator />
</PersistGate>
</Provider>
);
}
}
I have tried removing the empty object {} passed to createStore, setting it as undefined, and setting the initial values here instead, but nothing seems to work. Any guidance appreciated.
I have been following this tutorial to integrate redux into my react native app.
https://github.com/jlebensold/peckish
On my Home view, I'm not able to call the functions from my action folder.
One difference is that I'm using react-navigation in my app. Wonder if I need to integrate redux with react navigation to be able to use redux for all data?
Below is the full implementation code I have been doing.
On the Home screen, I call the fetchSite function on ComponentDidMount to launch an async call with axios. But I can't even access to this function.
Sorry for this long post but I can't figure out how to make this work so quite difficult to make a shorter code sample to explain the structure of my app.
Let me know if any question.
index.ios.js
import React from 'react'
import { AppRegistry } from 'react-native'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware, compose} from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import reducer from './app/reducers'
import AppContainer from './app/index'
// middleware that logs actions
const loggerMiddleware = createLogger({ predicate: (getState, action) => __DEV__ });
function configureStore(initialState) {
const enhancer = compose(
applyMiddleware(
thunkMiddleware, // lets us dispatch() functions
loggerMiddleware,
),
);
return createStore(reducer, initialState, enhancer);
}
const store = configureStore({});
const App = () => (
<Provider store={store}>
<AppContainer />
</Provider>
);
AppRegistry.registerComponent('Appero', () => App;
reducers/index.js
import { combineReducers } from 'redux';
import * as sitesReducer from './sites'
export default combineReducers(Object.assign(
sitesReducer,
));
reducers/sites.js
import createReducer from '../lib/createReducer'
import * as types from '../actions/types'
export const searchedSites = createReducer({}, {
[types.SET_SEARCHED_SITES](state, action) {
let newState = {};
action.sites.forEach( (site) => {
let id = site.id;
newState[id] = Object.assign({}, site, { id });
});
return newState;
},
});
../lib/createReducer
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
../actions/types
export const SET_SEARCHED_SITES = 'SET_SEARCHED_SITES';
AppContainer in ./app/index
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ActionCreators } from './actions';
console.log(ActionCreators); //Properly gathered the functions from the actions folder
import { Root } from './config/router';
window.store = require('react-native-simple-store');
window.axios = require('axios');
class App extends Component {
render() {
return (
<Root />
)
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapDispatchToProps)(App);
ActionCreators in './actions';
import * as SiteActions from './sites'
export const ActionCreators = Object.assign({},
SiteActions,
);
Actions in './actions/sites'
import * as types from './types' //See above
export function fetchSites(token) {
return (dispatch, getState) => {
let instance = axios.create({
baseURL: url + 'api/',
timeout: 10000,
headers: {'Accept' : 'application/json', 'Authorization' : 'Bearer ' + token}
});
instance.get('/sites?page=1')
.then(response => {
console.log(response.data.data);
dispatch(setSearchedSites({sites: response.data.data}));
}).catch(error => {
console.log(error);
});
}
}
export function setSearchedSites({ sites }) {
return {
type: types.SET_SEARCHED_SITES,
sites,
}
}
Root file for navigation based on react-navigation
I made it as simple as possible for this example.
import React from 'react';
import {StackNavigator} from 'react-navigation';
import Home from '../screens/Home';
export const Root = StackNavigator({
Home: {
screen: Home,
}
});
And finally my Home screen
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {Text, View} from 'react-native';
class Home extends Component {
componentDidMount()
{
let token = "12345678" //Just for this example
this.props.fetchSites(token).then( (response) => {
console.log(response);
});
}
render() {
return (
<View>
<Text>This is the Home view</text>
</View>
);
}
}
function mapStateToProps(state) {
return {
searchedSites: state.searchedSites
};
}
export default connect(mapStateToProps)(Home);
To use action methods you need to connect in home screen like this
import { fetchSites } from '<your-path>'
// your Home's other code.
const mapDispatchToProps = (dispatch) => {
return{
fetchSites:dispatch(fetchSites())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Home);
after that you can use fetchSites as this.props.fetchSites whenever you want.
On running the below code, i end up with the error message "Requested keys of a value that is not an object" in CategoryList.js.
I believe "stores.dispatch(CategoryAction.categoryView())" in CategoryContainer.js should set the value to props category, but this.props.categories in CategoryList.js returns null value which causes the error.
ConfigureStore.js:
import {createStore, applyMiddleware} from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(reducers);
export default store;
CategoryContainer.js:
import React, { Component } from 'react';
import stores from '../stores/ConfigureStore';
import * as CategoryAction from '../actions/CategoryAction';
stores.dispatch(CategoryAction.categoryView());
class CategoryContainer extends Component {
render() {
return (
<CategoryList/>
);
}
}
const mapStateToProps = (state) => {
return {
categories: state.categories,
};
}
const mapDispatchToProps = (dispatch) => {
return {
bindActionCreators(CategoryAction, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryContainer);
CategoryAction.js:
import * as actionTypes from './ActionTypes';
import AppConstants from '../constants/AppConstants';
export function categoryView() {
const categories = [{name:'CATEGORY', type:'CATGORY_TYPE'}];
return {
type: "CATEGORY_VIEW",
categories: categories
};
}
CategoryReducer.js:
const initialState = {
categories:[]
}
export default function categoryReducer (state = initialState, action) {
switch (action.type) {
case "CATEGORY_VIEW":
return Object.assign({}, state, {
categories: action.categories
});
}
}
CategoryList.js:
import React, {Component} from 'react';
import {
Text, View, TouchableHighlight, TouchableOpacity, ListView, StyleSheet
} from 'react-native';
import * as AppConstants from '../constants/AppConstants';
import CategoryAdd from '../components/CategoryAdd';
export default class CategoryList extends Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
this.state = {
dataSource: this.ds.cloneWithRows(this.props.categories),
}
}
}
I even tried as below in CategoryContainer.js, but it doesn't help. Still got the same error,
<CategoryList {...this.props}/>
But if i change the CategoryList.js by replacing this.props.categories with constant value as below, it works.
const categories = [{name:'CATEGORY', type:'CATGORY_TYPE'}];
this.state = {
dataSource: this.ds.cloneWithRows(categories),
}
Kindly assist to set values in this.props.categories on redux flow.
You should update the categories array in reducer like this -
return Object.assign({}, state, {
categories: Object.assign([], state.categories, action.categories)
});
And then do <CategoryList {...this.props}/> in container.
Below changes in CategoryContainer.js makes it works,
categories: state.categoryReducer.categories,
modal: state.categoryReducer.modal