I have react-native + redux application. In this application i have about 10 components (music, movies etc.)
But, i can't correctly connect redux to react-native. I have error "could not find store in either the context or props of..."
My App.js file
import App from './src/index';
export default App;
And my index.js file
import React, { Component } from 'react'
import { View } from 'react-native'
import { AppNavigator } from './nav/nav'
import { createStore, applyMiddleware } from 'redux';
import { Provider, connect } from 'react-redux';
import axios from 'axios';
import axiosMiddleware from 'redux-axios-middleware';
import reducer from './store/reducer';
import { listPosts } from './store/reducer';
const client = axios.create({
baseURL: 'https://xxxxxx.xxx',
responseType: 'json'
});
const store = createStore(reducer, applyMiddleware(axiosMiddleware(client)));
class App extends Component {
render() {
return (
<Provider store={store}>
<View style={{ flex: 1 }}>
<AppNavigator />
</View>
</Provider>
)
}
}
const mapStateToProps = state => {
let storedPosts = state.posts.map(repo => ({ key: repo.id, ...repo }));
return {
posts: storedPosts
};
};
const mapDispatchToProps = {
listPosts
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Related
This is my error:
ERROR Error: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(App) in connect options.
This error is located at:
in Connect(App)
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in ReactNativeApp(RootComponent)
ERROR TypeError: (0, _$$_REQUIRE(_dependencyMap[4], "./Redux/store").configureStore) is not a function. (In '(0, _$$_REQUIRE(_dependencyMap[4], "./Redux/store").configureStore)()', '(0, _$$_REQUIRE(_dependencyMap[4], "./Redux/store").configureStore)' is undefined)
ERROR Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication). A frequent cause of the error is that the application entry file path is incorrect.
This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.
This is my code
index.js
import { AppRegistry } from 'react-native';
import React from 'react';
import App from './Redux/App';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';
import { configureStore } from './Redux/store';
const store = configureStore()
const RNRedux = () => (
<Provider store = { store }>
<App />
</Provider>
)
AppRegistry.registerComponent(appName, () => RNRedux);
redux/App.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
Button,
Text
} from 'react-native';
import { connect, Provider } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as countActions from './Action';
class App extends Component {
decrementCount() {
let { count, actions } = this.props;
count--;
actions.changeCount(count);
}
incrementCount() {
let { count, actions } = this.props;
count++;
actions.changeCount(count);
}
render() {
const { count } = this.props;
return (
<View styles={styles.container}>
<Button
title="increment"
onPress={() => this.incrementCount()}
/>
<Text style={styles.textCenter}>{count}</Text>
<Button
title="decrement"
onPress={() => this.decrementCount()}
/>
</View>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
textCenter: {
textAlign: 'center'
}
});
const mapStateToProps = state => ({
count: state.count.count,
});
const ActionCreators = Object.assign(
{},
countActions,
);
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(ActionCreators, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(App)
Action.js
import { COUNTER_CHANGE } from './ActionType';
export function changeCount(count) {
return {
type: COUNTER_CHANGE,
payload: count
}
}
ActionType.js
export const COUNTER_CHANGE = 'COUNTER_CHANGE'
Reducer.js
import { COUNTER_CHANGE } from './ActionType';
const initialState = {
count: 0
};
const countReducer = (state = initialState, action) => {
switch(action.type) {
case COUNTER_CHANGE:
return {
...state,
count:action.payload
};
default:
return state;
}
}
export default countReducer;
store.js
import { createStore, combineReducers } from 'redux';
import countReducer from './Reducers'
const rootReducer = combineReducers(
{ count: countReducer }
);
const configureStore = () => {
return createStore(rootReducer);
}
export const store= createStore(configureStore);
How to solve this error and where is my error I don't know where I do a mistake
I'm doing a project like an example but it is it gives me an error
can you give me the suggestion on where I do a mistake and where something happen wrong
I'm adding redux dispatch and using useSelector in a class component with react native I'm trying to do many solutions but I don't understand properly how to do redux in-class component with react native
Issue
The code in your store.js file is a bit wonky.
There is no named configureStore export.
A store reference is exported, but it's an odd self-referencing amalgam of createStore and a custom store configurator function.
import { createStore, combineReducers } from 'redux';
import countReducer from './Reducers';
const rootReducer = combineReducers({
count: countReducer
});
const configureStore = () => {
return createStore(rootReducer); // <-- calls createStore again??
}
// This is the only export, but it oddly is passed the
// configureStore function instead of the Redux arguments
export const store = createStore(configureStore);
Solution
It's probably best to just create and export the store with the root reducer function.
import { createStore, combineReducers } from 'redux';
import countReducer from './Reducers';
const rootReducer = combineReducers({
count: countReducer
});
export const store = createStore(rootReducer);
Import store and pass to the Provider component.
import { AppRegistry } from 'react-native';
import React from 'react';
import App from './Redux/App';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';
import { store } from './Redux/store'; // <-- import named export store
const RNRedux = () => (
<Provider store={store}>
<App />
</Provider>
);
I think I should understand your problem.
You make the same name of two components (APP), and you can change one component or screen.
Try this code in your store.js
import { createStore, combineReducers } from 'redux';
import countReducer from './Reducers'
const rootReducer = combineReducers(
{ count: countReducer }
);
export const store= createStore(rootReducer);
Hello i am just starting to learn redux and am currently having a problem, i have an api i want to get information from and use it in different components i would appreciate if you help me
import React from 'react';
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from "redux-thunk";
import { createLogger } from "redux-logger";
import { BrowserRouter} from "react-router-dom";
import Reducer from './Reducers';
import App from './App';
import fetchSimcards from './Actions/fetchSimcards';
const middleware = [ thunk ];
middleware.push( createLogger() );
const store = createStore(
Reducer
applyMiddleware(...middleware),
);
import * as serviceWorker from './serviceWorker';
store.dispatch(fetchSimcards());
render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
and this is my action file
import * as type from '../Constans/ActionTypes';
export const ReceiveSimcards = Simcards => ({
type: type.RECEIVE_SIMCARDS,
Simcards
});
this is my reducer file
import { combineReducers } from "redux";
const Simcards = ( state = {}, action ) => {
console.log( state, action );
return state;
};
export default combineReducers({
Simcards
});
this is my container file for simcards
import React, {Component} from 'react';
import SimcardList from "../Component/SimcardList";
import { connect } from "react-redux";
class SimcardContainer extends Component {
render() {
const Simcards = this.props;
return (
<div>
<SimcardList title={"Simcards"} />
<div className="TableNumberItem">{Simcards.SimCardNumber}</div>
<div className="TableNumberItem">{Simcards.SimCardDescription}</div>
<div className="TableNumberItem">{Simcards.TeammatePrice}</div>
</div>
);
}
}
export default connect()(SimcardContainer);
and i want show this container in home page
With redux, you should call all API and handling logic code in action.
Example with action fetchAPI:
export const fetchAPI = () = async dispatch => {
let response = null;
try {
response = await axios.get('api/...')
// Example use axios
dispatch(fetchSuccess(response.data))
// To handle in reducer with redux
} catch (error) {
... Handle error here
}
}
const fetchSuccess = data => ({
type: FETCH_SUCCESS,
data: response.data
})
And in your component, you can use connect to get state and action:
import { bindActionCreators } from 'redux';
import React, { Component } from 'react';
import SimcardList from "../Component/SimcardList";
import { connect } from "react-redux";
import * as _Actions from '../../action/index'
class SimcardContainer extends Component {
componentDidMount(){
const { fetchAPI } = this.props.actions;
**fetchAPI();** // Call API here
}
render() {
const { stateReducer} = this.props;
console.log(stateReducer)
// Here, you will see data that you handled in reducer
// with action type FETCH_SUCCESS
// You should remember data that you fetch from API is asynchronous,
// So you should check like that `data && {do any thing heree}`
return (
<div>
<SimcardList title={"Simcards"} />
<div className="TableNumberItem">{Simcards.SimCardNumber}</div>
<div className="TableNumberItem">{Simcards.SimCardDescription}</div>
<div className="TableNumberItem">{Simcards.TeammatePrice}</div>
</div>
);
}
}
const mapStateToProps = state => ({
stateReducer: state
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(_Actions, dispatch)
})
export default connect(mapStateToProps, mapDispatchToProps)(SimcardContainer)
i'm currently working on an app using react-native for the first time,
but i'm struggling to add redux.
i get this error TypeError: Cannot read property 'getState' of undefined and i don't know how to fix it.
this my code :
import React from "react";
import Home from "./home";
import { store } from "./redux/store";
import { Provider } from "react-redux";
/* #flow */
import { View, StyleSheet } from "react-native";
class App extends React.Component {
render() {
return (
<Provider store={store}>
<View style={styles.container}>
<Home />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
store.js :
import { applyMiddleware, createStore } from "redux";
import logger from "redux-logger";
import reducer from "./reducer";
const middlewares = [logger];
const store = createStore(reducer, applyMiddleware(...middlewares));
export default store;
reducer.js :
import { combineReducers } from "redux";
import mapReducer from "../redux/maps/maps-reducer";
export default combineReducers({
map: mapReducer
});
maps-action.js:
import MAPSActionTypes from './maps-action-types';
export const currentlocation = () => ({
console.log(${location});
type : MAPSActionTypes.GET_CURRENT_LOCATION
});
maps-reducer.js:
import MAPSActionTypes from "./mapsactiontypes";
const INITIAL_STATE = {
location: {}
};
const mapReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case MAPSActionTypes.GET_CURRENT_LOCATION:
return {
...state,
location: action.payload
};
default:
return state;
}
}
export default mapReducer;
home.js:
import React from 'react';
import {
StyleSheet,
View,
Text,
} from 'react-native';
import {connect} from 'react-redux'
const Home = (props) => {
return (
<View style={styles.container}>
<Text>welcome</Text>
<Text>{props.location}</Text>
</View>
);
};
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center'
}
});
const mapStateToProps = (state) => {
return {
location: state.map.location
}
}
export default connect(mapStateToProps)(Home);
i'm all ears from more clarification or more details.
i will be very thankful if there's anyone who can help me to fix that problem.
The problem is you export default in store.js file.
export default store;
But in App you import it with {}. So the store you imported is undefined.
import { store } from "./redux/store";
The correct import is
import store from "./redux/store";
On onClick SigninButton calls ONTOGGLE_MODAL_SIGNIN which updates ui.isSigninModalActive from store. Everything works fine but I see that my whole app gets re-rendered when ui.isSigninModalActive toggles on and off. Is this normal behaviour? I had thought that you have to store.subscribe and update that component's inner state and that component alone gets updated (and not the whole app) when store updates. If the whole app re-renders, then what is the point of store.subscribe? Or did I mess up somewhere? Thanks for the help in advance.
signin_button.jsx
import React, { Component } from 'react';
import { store } from '../../../store/store';
import { onToggleModal } from '../../../actions/ui';
export const SigninButton = () => (
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" className="signin-button"
onClick={ () => store.dispatch(onToggleModal('signin')) }>
<path d="M0 0h24.997C25.55 0 26 .444 26 1c0 .552-.45 1-1.003 1H0V0"/>
</svg>
);
router.js
import React from 'react';
import { render } from 'react-dom';
import { Router, IndexRoute } from 'react-router';
import { Provider } from 'react-redux';
import { store, history } from '../../store/store';
import App from '../../ui/containers/app_container';
import { Welcome } from '../../ui/pages/welcome';
Meteor.startup(() => {
render(
<Provider store={ store }>
<Router history={ history }>
<Route path="/" component={ App }>
<IndexRoute component={ Welcome } />
</Route>
</Router>
</Provider>,
document.getElementById('root'));
});
store.js
import { createStore } from 'redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import { rootReducer } from '../reducers/root_reducer';
import { ui } from './ui_store';
const defaultState = { ui };
export const store = createStore(rootReducer, defaultState,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export const history = syncHistoryWithStore(browserHistory, store);
ui_store.js
export const ui = {
isSigninModalActive: false,
};
root_reducer.js
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { ui } from './ui_reducer';
export const rootReducer = combineReducers({ ui, routing: routerReducer });
ui_reducer.js
import update from 'immutability-helper';
import { toggleBodyOverflow } from '../modules/toggle_body_overflow';
export const ui = (state = null, action) => {
switch (action.type) {
case 'ONTOGGLE_MODAL_SIGNIN': {
toggleBodyOverflow(!state.isSigninModalActive);
document.getElementById('signin-modal__container').classList.toggle('active');
return update(state, { isSigninModalActive: { $set: !state.isSigninModalActive } });
}
default: return state;
}
};
ui_action.js
export const onToggleModal = modal => ({ type: `ONTOGGLE_MODAL_${modal.toUpperCase()}` });
EDIT: I found the reason why app is re-rendering
On my app container, I have set mapStateToProps and sent the state.ui down the components as props. I "fixed" it by removing it. Is this the correct way to stop re-rendering the whole app?
app_container.js
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as ui from '../../actions/ui';
import { App } from '../layouts/app_layout';
// problem: const mapStateToProps = state => { ui: state.ui };
const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => bindActionCreators(ui, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(App);
I am using react-router and react-router-redux to handle navigation on my page. I need change my url programmatically inside component. I was trying to use this method: history.push to achieve this but this method is only change the url and component associated with this url is not updated. This app is simple list with pagination so when i switch to the next page url is changing for example /posts/1 to /posts/2 but view is not updated. I think this should work like this:
User click pagination item and click handler is called passing
page number as argument
Inside click handler i call history.push(/posts/[page]). I could
use Link component but i want to be able to do something when user
click pagination item
I expect that my ObjectList component will be mounted again and
componentDidMount will be called
This is probably not the best aproach so i will be greatfull for tips
links are hardcoded especially first argument
My source code:
client.js
import React from "react";
import ReactDOM from "react-dom";
import {Router, Route, IndexRoute, browserHistory} from "react-router";
import Results from "./views/Results";
import Home from "./views/Home";
import App from './components/App'
import { Provider } from 'react-redux';
import store, { history } from './store';
const app = document.getElementById('app');
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/:category/:cityId/:pageNum" component={Results}></Route>
</Route>
</Router>
</Provider>,
app
);
store.js
import { createStore, compose, applyMiddleware } from 'redux'
import { syncHistoryWithStore } from 'react-router-redux'
import thunkMiddleware from 'redux-thunk'
import { browserHistory } from 'react-router'
import rootReducer from './reducers/index'
import createLogger from 'redux-logger'
import categories from './data/categories'
const loggerMiddleware = createLogger()
const defaultState = {
categories,
resultsList: {
objects: [],
counters: [],
isFetching: false
}
};
const store = createStore(
rootReducer,
defaultState,
compose (
applyMiddleware(
thunkMiddleware,
loggerMiddleware
),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
);
export const history = syncHistoryWithStore(browserHistory, store)
export default store
ObjectList.js
import React from "react";
import ObjectItem from "../components/ObjectItem"
import Loader from "../components/Loader"
import fetchObjects from "../actions/actionCreators";
import switchUrl from "../actions/actionCreators";
import PaginationPanel from "../components/PaginationPanel"
import classNames from 'classnames'
import { push } from 'react-router-redux';
import { browserHistory } from 'react-router'
import store, { history } from '../store';
export default class ObjectList extends React.Component {
static defaultProps = {
objectsPerPage: 20,
objectContainerClassName: 'object_list_items'
}
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchObjects(this.props.params.pageNum);
}
paginateHandler(page) {
this.props.history.push('/hotele/1/'+page)
}
render() {
const { resultsList } = this.props
if(resultsList.items.length > 0) {
const ObjectComponents = resultsList.items.map((item) => {
return <ObjectItem key={item.post_id} {...item}/>;
});
const paginationComponent =
<PaginationPanel
{...this.props}
pageNum={Math.ceil(resultsList.counters.allPosts/this.props.objectsPerPage)}
pageClickedHandler={this.paginateHandler.bind(this)}
currentPage={parseInt(this.props.params.pageNum)}
/>
return (
<div className="object-lists">
<div className={this.props.objectContainerClassName}>
<div>{ObjectComponents}</div>
</div>
{paginationComponent}
</div>
)
}
else if(!resultsList.isFetching || resultsList.items.length === 0) {
return <Loader />;
}
}
}
Home.js
import React from "react"
import { Link } from "react-router"
const Home = React.createClass({
render() {
return (
<div>
Strona główna <br />
<Link to={`/hotele/1/1`}>Lista wyszukiwania</Link>
</div>
)
}
})
export default Home
Results.js
import React from "react";
import ObjectList from "../components/ObjectList"
import CategoryTabs from "../components/CategoryTabs"
import fetchObjects from "../actions/actionCreators"
export default class Results extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<div>
<CategoryTabs { ...this.props } />
<ObjectList { ...this.props } />
</div>
);
}
}
reducers/index.js
import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'
import objects from './objects'
import categories from './categories'
const rootReducer = combineReducers({objects, categories, routing: routerReducer})
export default rootReducer
reducers/objects.js
function objects(state = {
isFetching: false,
items: [],
counters: []
}, action) {
switch (action.type) {
case 'RECEIVE_OBJECTS':
return Object.assign({}, state, {
isFetching: false,
items: action.objects.posts,
counters: action.objects.counters
})
default:
return state;
}
}
export default objects
app.js
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actionCreators from '../actions/actionCreators';
import Main from '../components/Main';
function mapStateToProps(state) {
return {
resultsList: state.objects,
categories: state.categories
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
export default App;
actionCreators.js
import fetch from 'isomorphic-fetch'
import { push } from 'react-router-redux';
function receiveObjects(objects, json) {
return {
type: 'RECEIVE_OBJECTS',
objects
}
}
function requestObject(pageNum) {
return {
type: 'REQUEST_OBJECTS',
pageNum
}
}
export function fetchObjects(pageNum) {
return dispatch => {
dispatch(requestObject(pageNum));
let url = 'http://localhost:8080/posts?city=986283&type=hotel&page='+pageNum;
return fetch(url)
.then(response => response.json())
.then(json => dispatch(receiveObjects(json)));
}
}
ObjectList component will not be mounted again because you are not changing components tree. It is still
<Home>
<Results>
<ObjectList />
</Results>
</Home>
It will be remounted only if you go to a different route and mount different root component so the whole tree would change. But You're just passing different props. You need to use
componentWillReceiveProps(nextProps) {
this.props.fetchObjects(nextProps.params.pageNum);
}