I am new to using redux for React Native and am testing it with a simple case. I have been able to successfully connect to the store, and I can see the action is dispatched properly using the redux debugger, however, the store is not updating in the debugger. I've tried several different implementations, but nothing is working. Any help would be appreciated!
Component:
import React, { PureComponent } from 'react'
import { Text, TouchableOpacity, SafeAreaView, Alert, Button } from 'react-native'
import { Navigation } from 'react-native-navigation';
import { connect } from 'react-redux'
import simpleAction from '../store/actions/simpleAction'
class App2 extends PureComponent {
constructor(props){
super(props);
}
pressRedux = () => {
const data = 'hello'
this.props.simpleAction(data)
}
render() {
return (
<SafeAreaView>
<Text>
{this.props.state.simpleReducer.text}
</Text>
<Button onPress = {this.pressRedux} title = 'Redux' />
</SafeAreaView>
)
}
}
function mapStateToProps(state) {
return {
state: state
};
}
const mapDispatchToProps = {
simpleAction
}
export default connect(mapStateToProps, mapDispatchToProps)(App2);
Action:
import {SET_TEXT} from '../types/types'
export default function simpleAction(data) {
return({
type: SET_TEXT,
payload: data
})
}
reducer:
import SET_TEXT from '../types/types'
const INITIAL_STATE = {
text: 'Hi'
}
const simpleReducer = (state = INITIAL_STATE, action ) => {
switch(action.type){
case SET_TEXT:
return { ...state, text: action.payload};
default:
return state;
}
}
export default simpleReducer;
The code you've shared here looks correct. Only thing I can suggest is, if you're seeing the action come through in the debugger, your issue is either with the data/payload or logic within simpleReducer.
In this case you have it properly stripped down so I'd almost think this isn't actually the code you are running, it might be something in your build process?
Related
I encountered this problem when I was testing my newly created action and reducer. The prop is not being updated even though I'm setting it to a fixed value within my reducer.
Component:
class <ComponentName> extends Component {
componentDidMount() {
login()
}
render() {
if(this.props.isLogged)
return (
<App/>
);
else
return (
<ErrorScreen/>
);
}
}
function mapStateToProps(state) {
return {
isLogged:state.auth.isLogged
}
}
const mapDispatchToProps = (dispatch) => {
return {
login: () => dispatch(login())
};
};
export default connect(mapStateToProps,mapDispatchToProps)(<ComponentName>)
Action:
export function login() {
return {
type:"TEST"
}
}
Reducer:
const initState = {
isLogged: false,
}
export default (state=initState, action) => {
switch(action.type) {
case "TEST":
return {
...state,
isLogged: true
}
break;
default:
return state
}
}
Combine Reducer:
import {combineReducers} from 'redux'
import AuthenticationReducer from './authenticationReducer'
export default combineReducers({
auth: AuthenticationReducer
})
Provider:
import React, {Component} from "react";
import <ComponentName> from './app/screens/<ComponentName>'
import store from './app/store'
import {Provider} from 'react-redux'
export default () =>
<Provider store={store}>
<<ComponentName>/>
</Provider>;
Been trying to debug this for some time now. I still don't know why this is happening. Maybe I implemented it wrongly? If there are some files I forgot to include, please inform me. Thanks and have a nice day!
The reason your code isn't working as expected is because you're calling the login() action creator, rather than the login() method that is returned from mapDispatchToProps() (and injected into the props of <ComponentName/>).
Try revising your code by adding this.props before your call to login() like so:
class <ComponentName> extends Component {
componentDidMount() {
// Update this line here so that the login() method
// injected by connect() is called (ie via this.props)
this.props.login()
}
render() {
if(this.props.isLogged)
return <App/>
else
return <ErrorScreen/>
}
}
Our project is mainly written in AngularJS 1.5, but we are transitioning over to ReactJS. We're using react2angular to allow our AngularJS project to consume React components. Our project is also using ngRedux to store certain data. While I'm able to save certain data using Angular, I am unable to access the Redux store data via the mapped props.
this.props.interval in CardController.js is undefined. I am able to get store data using this.props.store.getState(), but React is not able to detect if there has been a change in the Redux store in componentWillReceiveProps, so I don't think that's an option.
CardContainer.js
import { connect } from 'react-redux';
import CardController from './CardController';
const mapStateToProps = (state) => {
return {
interval: state.interval
};
};
const mapDispatchToProps = null;
const ScorecardContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(CardController);
export default CardContainer;
CardController.js
import React from 'react';
import PropTypes from 'prop-types';
import Card from './Card';
export default class CardController extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: this.props.interval,
state: this.props.$scope.filterParams,
};
}
render() {
return (
<div className="CardController">
<Card
selected={this.state.selected}
/>
</div>
);
}
}
CardController.propTypes = {
interval: PropTypes.string,
};
reducers.js
import * as actionTypes from './actionTypes';
const initialState = {
interval : 'week'
};
export const setInterval = (state, action) => {
return {
...state,
interval : action.interval
};
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SET_INTERVAL :
return setInterval(state, action);
default :
return state;
}
};
export default reducer;
Turns out that the problem was that I forgot to drill down. Instead of state.interval, it should have been state.card.interval.
I am using 'react-fileupload' to upload files on my server. In case of success I receive response with content of this file. So in one component I want to upload file and change stores state and in another component I want to show that data.
But i don't know why my dispatch function doesn't work.
Component with uploader:
import React, { Component } from 'react';
import FileUpload from 'react-fileupload';
import { connect } from 'react-redux';
import { updateOverview } from '../actions/index';
import { bindActionCreators } from 'redux';
class Header extends Component {
render() {
const options = {
baseUrl: 'http://127.0.0.1:8000/api/upload_file',
chooseAndUpload: true,
uploadSuccess: function(res) {
console.log('success');
updateOverview(res.data);
},
uploadError: function(err) {
alert(err.message);
}
};
return (
<div>
<FileUpload options={options} ref="fileUpload">
<button
className="yellow darken-2 white-text btn-flat"
ref="chooseAndUpload">
Upload
</button>
</FileUpload>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ updateOverview }, dispatch);
}
export default connect(null, mapDispatchToProps)(Header);
Component where data is shown:
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Overview extends Component {
renderContent() {
console.log(this.props.overview);
if (!this.props.overview) {
return <div> Upload file!</div>;
}
return this.props.overview;
}
render() {
return (
<div>
<h1>Overview</h1>
{this.renderContent()}
</div>
);
}
}
function mapStateToProps({ overview }) {
return { overview };
}
export default connect(mapStateToProps)(Overview);
Action creator:
import { FETCH_OVERVIEW } from './types';
export function updateOverview(data) {
return { type: FETCH_OVERVIEW, payload: data };
}
reducer index.js
import { combineReducers } from 'redux';
import overviewReducer from './overviewReducer';
export default combineReducers({
overview: overviewReducer
});
overviewReducer.js
import { FETCH_OVERVIEW } from '../actions/types';
export default function(state = null, action) {
switch (action.type) {
case FETCH_OVERVIEW:
return action.payload;
default:
return state;
}
}
The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.
Your Header component already knows how to create action.
Considering the your Home component need ,your don't need of bindActionCreators.
The correct way to do this.
const mapDispatchToProps = dispatch => {
return {
callUpdateOverview: () => {
dispatch({ updateOverview });
}
}
}
And in the Header render method :
this.props.updateOverview(res.data);
EDIT :
In your Home Component render method,
const homeThis = this; //save `this` object to some variables
^^^^^^^^^^^^^^^^^^^^^^
const options = {
baseUrl: ..,
chooseAndUpload: ..,
uploadSuccess: function (res) {
homeThis.props.callUpdateOverview();// call using `homeThis`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
};
In your code, you are calling
updateOverview(res.data);
actually which should be called like,
this.props.updateOverview(res.data);
Because, the redux will listen only to the dispatch bound actions, so to enable that, we use connect function from react-redux package, so that redux will know to update itself upon the action execution.
connect will bind your action to the component props on this.props, so it is very essential to use this.props.action() and not just action()
I recently began learning how to use React-Native and Redux together. I got an error in the IOS simulator that I can't figure out how to fix, and I was wondering if anyone had seen this before.
Here's the error:
Provider does not support changing store on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
I followed that link mentioned in the error message, but it seemed like it needed me to be using Webpack. In the link, where it references if(module.hot), I got the following error when trying to use this solution:
Unable to resolve module
So I'm not sure where to go from here. My project so far is very small. I have my index.ios.js, then an app folder containing a components folder, a store folder and a reducer folder. The structure looks like this:
index.ios.js
app
store
index.js
component
index.js
reducer
index.js
Here is my code:
index.ios.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {configureStore} from './app/store';
import Main from './app/components/Main';
export default class introToRedux extends Component {
render() {
return (
<Provider store={configureStore()}>
<Main />
</Provider>
);
}
}
AppRegistry.registerComponent('introToRedux', () => introToRedux);
components/Main.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var Main = React.createClass({
render(){
return (
<View>
<Text>{this.props.text}</Text>
</View>
);
}
});
var mapStateToText = (state) => {
return {
text: state.text
}
}
module.exports = connect(mapStateToText)(Main);
reducer/index.js
module.exports = (state={}, action) => {
switch (action.type) {
default:
return state;
}
}
store/index.js
import {createStore} from 'redux';
import reducer from '../reducer';
var defaultState = {
text: "default text"
}
export var configureStore = (initialState=defaultState) => {
return createStore(reducer, initialState);
}
Any help on this would be awesome!
Why do you export configureStore()? You might as well
const initialState = {
text: "default text"
}
export default function reducer (state=initialState, action) {
switch (action.type) {
default:
return state;
}
}
createStore() should be executed once.
index.js
// import stuff
const store = createStore(reducer)
class IntroToRedux extends Component {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
ReactDOM.render(IntroToRedux, document.getElementById('root'))
I am starter in react & redux. I want to make todolist app. but when I insert a list into a store the displayList() function is not re-render. How can I fix this.
This is my reducer code.
export default function(state = ['start1','start2'], actions) {
switch(actions.type) {
case 'APPEND_ITEM':
state.push(actions.payload.item)
return state
break
}
return state
}
And this is my todolist.js code.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import ReactDOM from 'react-dom'
import React from 'react'
class TodoList extends React.Component {
displayList(){
return this.props.dispatchs.map((item) => {
return(
<li key={ Math.random() }>{item}</li>
)
})
}
render(){
return(
<div>
{ this.displayList() }
</div>
)
}
}
var mapStateToProps = (state) => {
return {
dispatchs: state.dispatch
}
}
export default connect(mapStateToProps)(TodoList)
pin 2,3 does not re-render on the screen
Thank you for help.
Instead of using push in the reducer, return [ ... state, action.payload.item ]
case 'APPEND_ITEM':
return [ ...state, action.payload.item ]
This is because React will see the new state (object reference) equal to the old one, and decide not to re-render since it seems like nothing have changed