Before Calling createReduxContainer please call createReactNavigationReduxMiddleware so that we know when to trigger your listener
I am trying to integrate react-navigation v3 with redux and this above problem is showing on my simulator
This is a tested sample of react-navigation(v3) and Redux. Here the react-navigation is integrated into Redux. However, I declined to use this method anymore. I believe integrating is not necessary when it is going to make things more complicated.
App.JS file :
import React, {Component} from 'react';
import { Provider } from "react-redux";
import { createStore, applyMiddleware, combineReducers } from "redux";
import { createStackNavigator } from "react-navigation";
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
createNavigationReducer
} from "react-navigation-redux-helpers";
import MainScreen from "./Screens/MainScreen";
import SignUpScreen from "./Screens/SignUpScreen";
import rootReducer from "./src/rootReducer";
import ReduxNavigation from "./src/ReduxNavigation";
import { Directions } from 'react-native-gesture-handler';
const AppNavigator = createStackNavigator(
{
// LoginScreen: { screen: LoginScreen },
MainScreen: { screen: MainScreen },
SignUpScreen: { screen: SignUpScreen }
},
{
initialRouteName: "SignUpScreen"
}
);
const navReducer = createNavigationReducer(AppNavigator);
const appReducer = combineReducers({
nav: navReducer,
app: rootReducer
});
const middleware = createReactNavigationReduxMiddleware(
"root",
state => state.nav
);
export const AppNav = reduxifyNavigator(AppNavigator, "root");
const store = createStore(appReducer, applyMiddleware(middleware));
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<ReduxNavigation />
</Provider>
);
}
}
ReduxNavigation.js :
import React from "react";
import { BackHandler } from "react-native";
import { connect } from "react-redux";
import { NavigationActions } from "react-navigation";
import { AppNav } from "../App";
class ReduxNavigation extends React.Component {
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
onBackPress = () => {
const { nav, dispatch } = this.props;
if (nav.index === 0) {
return false;
}
dispatch(NavigationActions.back());
return true;
};
render() {
const { nav, dispatch } = this.props;
return <AppNav state={nav} dispatch={dispatch} />;
}
}
const mapStateToProps = state => ({
nav: state.nav
});
export default connect(mapStateToProps)(ReduxNavigation);
this code also support back button feature for Android devices.
Related
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";
I'm trying to understand and use HOC with react and redux. I have the following setup. I will have multiple buttons using the hoc and passing in their own onClick. and would like to know:
a. Is this a good usage of the HOC pattern?
b. with this setup inside of the render function of FooterButton, this references DesignatedWorkerGlobalScope and iconHeigh, iconWidth, and iconColor inside HomeButton become either undefined or unexpected values.
Any advice would be appreciated.
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { getColors, getStylesheet} from "../../styles/StyleManager";
const FooterButtonWrapper = (FooterButtonWrapped, onClick) => {
return class FooterButton extends React.Component {
constructor(props) {
super(props);
this.state = {
Theme: getStylesheet(),
colors: getColors()
}
}
_onClick = () => {
onClick(this.props.dispatch);
}
render() {
const { Theme, colors } = this.state;
return(
<TouchableOpacity onPress={this._onClick}>
<FooterButtonWrapped iconWidth={15} iconHeight={15} iconColor={"white"}/>
</TouchableOpacity>
)
}
}
}
const mapStateToProps = (state, ownProps) => ({});
const composeFooterButton = compose(
connect(mapStateToProps),
FooterButtonWrapper
);
export default composeFooterButton;
and then a button that uses it:
import React from 'react';
import { View, Text } from 'react-native';
import { push as gotoRoute } from 'react-router-redux';
import { FooterButtonWrapper } from './';
import { Home } from '../../assets';
const HomeButton = ({iconHeight, iconWidth, iconColor}) => (
<View>
<Home width={ iconWidth } height={ iconHeight } color={ iconColor }/>
<View><Text>Home</Text></View>
</View>
);
const _onClick = dispatch => {
dispatch( gotoRoute( '/' ));
}
export default FooterButtonWrapper(HomeButton, _onClick);
It happens so, as you use
const composeFooterButton = compose(
connect(mapStateToProps),
FooterButtonWrapper
);
export default composeFooterButton;
for HOC function instead of Component.
Both compose and connect can wrap your Component.
So your HOC could be:
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { getColors, getStylesheet } from '../../styles/StyleManager';
export default function FooterButtonWrapper(FooterButtonWrapped, onClick) {
class FooterButton extends React.Component {
constructor(props) {
super(props);
this.state = {
Theme: getStylesheet(),
colors: getColors(),
};
}
_onClick = () => {
onClick(this.props.dispatch);
};
render() {
const { Theme, colors } = this.state;
return (
<TouchableOpacity onPress={this._onClick}>
<FooterButtonWrapped
iconWidth={15}
iconHeight={15}
iconColor={'white'}
/>
</TouchableOpacity>
);
}
}
const mapStateToProps = (state, ownProps) => ({});
return connect(mapStateToProps)(FooterButton);
}
How can I get access to props of a root component?
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import cartReducer from './store/cartReducer'
import AddToCart from './cart/addToCart.jsx'
const store = createStore(cartReducer);
render(<Provider store={store}><AddToCart clicked={this.props.onIncrementCounter} />
</Provider>, document.getElementById('addToCart'));
Is it possible? Because I have an error: " Cannot read property 'props' of undefined".
I am new in React.
Here is AddToCart component
import React, { Component } from 'react';
import { connect } from 'react-redux';
class AddToCart extends Component {
constructor(props) {
super(props);
}
}
const mapStateToProps = state => {
return {
items: state.cartItems,
count: state.cartItemCount
};
};
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT' })
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AddToCart);
you got this error " Cannot read property 'props' of undefined" because in that part of your code : <AddToCart clicked={this.props.onIncrementCounter} />
this is referencing anything
you can try this approach:
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import cartReducer from './store/cartReducer'
import AddToCart from './cart/addToCart.jsx'
const store = createStore(cartReducer);
render(<Provider store={store}><AddToCart/>
</Provider>, document.getElementById('addToCart'));
AddToCart.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
class AddToCart extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div onClick={this.props.onIncrementCounter}>
Increment
</div>
)
}
}
const mapStateToProps = state => {
return {
items: state.cartItems,
count: state.cartItemCount
};
};
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT' })
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AddToCart);
I'm trying to facebook login with my expo react native app. But i have a problem. I used redux. My app.js here.
import { createBottomTabNavigator, createAppContainer, createStackNavigator
} from 'react-navigation';
import AuthScreen from './screens/AuthScreen';
import { Provider } from 'react-redux'
import store from './store';
const MainNavigator = createBottomTabNavigator({
welcome: {screen: WelcomeScreen},
auth: { screen: AuthScreen },
main: {
screen: createBottomTabNavigator({
map: { screen: MapScreen },
deck: { screen: DeckScreen },
review: {
screen: createStackNavigator({
review: { screen: ReviewScreen },
settings: { screen: SettingsScreen }
})
}
})
}
})
class App extends React.Component {
render() {
return (
<Provider store={store} >
<View>
<MainNavigator/>
</View>
</Provider>
);
}
}
export default createAppContainer(MainNavigator)
And this is my AuthScreen.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import * as actions from '../actions/auth_actions';
class AuthScreen extends Component {
componentDidMoun(){
this.props.facebookLogin();
}
render(){
return(
<View>
<Text>Auth</Text>
</View>
)
}
}
export default connect(null, actions)(AuthScreen);
I called facebookLogin() function inside my auth_action.js file
export const facebookLogin = () => async dispatch => {
let token = await AsyncStorage.getItem('fb_token');
if(token) {
dispatch({ type: FACEBOOK_LOGIN_SUCCESS, payload: token })
}else{
doFacebookLogin(dispatch);
}
}
reducer/index.js file
import { combineReducers } from 'redux';
export default combineReducers({
auth: () => { return {} }
});
And this is my store.js file.
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers';
const store = createStore(
reducers,
{},
compose(
applyMiddleware(thunk)
)
);
export default store;
But i'm getting this error.
Invariant Violation: Could not find "store" in the context of "Connect(AuthScreen)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(AuthScreen) in connect options.
What is a problem? Can you help me?
You have not connected your store to this component export default connect(null, actions)(AuthScreen); and you have passed null where you should pass some mapper and just action where you should define which functions you want to import from reducer.
import React, { Component } from "react";
import { connect } from "react-redux";
import * as actions from "../actions";
import {
createBottomTabNavigator,
createAppContainer,
createStackNavigator
} from "react-navigation";
import AuthScreen from "../screens/AuthScreen";
import HomeScreen from "../screens/HomeScreen";
import SettingsScreen from "../screens/SettingsScreen";
import DeckScreen from "../screens/DeckScreen";
import MapScreen from "../screens/MapScreen";
import ReviewScreen from "../screens/ReviewScreen";
const AppNavigator = createBottomTabNavigator({
home: { screen: HomeScreen },
auth: { screen: AuthScreen },
main: {
screen: createBottomTabNavigator({
map: { screen: MapScreen },
deck: { screen: DeckScreen },
review: {
screen: createStackNavigator({
review: { screen: ReviewScreen },
settings: { screen: SettingsScreen }
})
}
})
}
});
const AppContainer = createAppContainer(AppNavigator);
enter code here
class AppNavigation extends Component {
render() {
return <AppContainer screenProps={this.props} />;
}
}
export default connect(
null,
actions
)(AppNavigation);
import React, { Component } from "react";
import { Provider } from "react-redux";
import AppNavigation from "./navigation/AppNavigation";
import store from "./store";
class App extends Component {
render() {
return (
<Provider store={store}>
<AppNavigation />
</Provider>
);
}
}
export default 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);
}