Hi when i console log my components props (passed down from redux) i get the initial state which is null. however using the react inspector i have the result of the axios request. I tried reading dozens of similar problems but cannot seen to resolve my issue.
Actions
import { searchService } from '../api/searchService';
export const actions = {
FETCH_USERS: 'FETCH_USERS',
}
export const searchUsers = () => dispatch => {
searchService.get('/search')
.then((result) => {
dispatch({
type: actions.FETCH_USERS,
payload: result
})
})
}
Reducers
import { actions } from '../actions';
export default (state = null, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return action.payload;
default:
return state;
}
}
Search Component
function mapStateToProps ({search}) {
return {search};
}
const mapDispatchToProps = dispatch => ({
searchUsers: () => dispatch(searchUsers())
});
export default connect(mapStateToProps, mapDispatchToProps)(withAuth()(Search));
Your problem is in the Reducer
First you should make an initial state, and then you need to edit this state in order for redux to feel the changes and update
Check the code below and let me know if it worked for you.
import { actions } from '../actions';
const INITIAL_STATE= {search: ""};
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case actions.FETCH_USERS:
return {...state, search: action.payload};
default:
return state;
}
}
Related
I am new in redux.
My code :
Home Screen
<Text> {{this.props.mycity}} </Text>
const mapStateToProps = function(state) {
return {
mycity: state.layersFlag.baseDistrictADhabi //consist true/false
}
}
export default connect(mapStateToProps)(HomeScreen);
Sidemenu Screen :
UI
<Switch onValueChange={(flag) => {
this.props.toggleCity();
} value={this.state.city} />
const mapDispatchToProps = dispatch => {
return {
toggleCity: () => {
dispatch({ type: "changeCity" })
}
};
};
export default connect(null, mapDispatchToProps)(SideMenuScreen);
Store and reducer setup :
const initialState = {
city : {
mycity: true
}
};
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload.mycity
})
default:
return state;
}
}
const Store = createStore(reducer);
I am stuck in sidemenu. How to dispach in mapDispatchToProps method:
How to pass action in mapDispatchToProps in sidemenu?
If my assumptions on what your Switch component does is correct, it would trigger the onValueChange event-listener when you pass in this.state.city to the value prop. You end up calling this.props.toggleCity() to dispatch your changeCity action. I think the set-up is correct for here...
However, it looks like your reducer is expecting an action.payload which you never passed in as part of the action.
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload.mycity
})
default:
return state;
}
}
So yes the dispatch is working correctly, but you are not passing all the necessary data for your reducer to return a new piece of state.
You need to update your mapDispatchToProps, your event-handler and your reducer to something like
<Switch onValueChange={(flag) => {
this.props.toggleCity(this.state.city);
} value={this.state.city} />
const mapDispatchToProps = dispatch => {
return {
toggleCity: (myCity) => {
dispatch({ type: "changeCity", payload: myCity })
}
};
};
export default connect(null, mapDispatchToProps)(SideMenuScreen);
Your reducer also seems to have an extra key, you don't need to access the mycity prop in payload if its already the payload. Update to:
const reducer = (state = initialState, action)=>{
switch (action.type) {
case "changeCity":
return Object.assign({}, state, {
mycity: action.payload
})
default:
return state;
}
}
Adding on, if you want your Hone component to re-render with the new data in your redux-state, you can do something like this.
In your HomeScreen component, make use of a state-variable to save your abudhabi or whatever city-value and call componentDidUpdate() to setState and re-render your component.
class HomeScreen extends React.Component{
state = {
abudhabi: false
}
//when the component gets the new redux state this will trigger
componentDidUpdate(prevProps){
if(this.props.abudhabi !== prevProps.abudhabi){
this.setState({
abudhabi: this.props.abudhabi
})
}
}
}
I implemented async reducer and i am trying to return asyncstorage value but when i call dispatcher it prints value in console but don't update initial state.
userReducer.js :
import {AsyncStorage} from "react-native";
const initialState = {
fullname: null
}
const userReducer = async (state = initialState, action) => {
switch (action.type) {
case 'GET_USERNAME':
const uname= await AsyncStorage.getItem('#uinfo:name', '')
console.log('uname: '+uname)
return { fullname: uname}
}
return state
}
export default userReducer;
In above console prints users name but dont't set the state.
I tried to Set dispatch in componentDidMount:
async componentDidMount() {
const uname= await this.props.getNAME()
console.log('uname: '+ this.props.fullname)
}
console.log('uname: '+ this.props.fullname) return undefined
My mapDispatchToProps:
function mapDispatchToProps(dispatch) {
return {
getNAME: () => dispatch({ type: 'GET_UNAME' })
}
}
Do i missing something, Why the state is not setting
fullname is an element from state as you assign initialState to state, so you can access fullname like this.
import {AsyncStorage} from "react-native";
const initialState = {
fullname: null
}
const userReducer = async (state = initialState, action) => {
switch (action.type) {
case 'GET_USERNAME':
const uname= await AsyncStorage.getItem('#uinfo:name', '')
console.log('uname: '+uname)
state.fullname = uname;
return { ...state }
}
return state
}
export default userReducer;
AsyncStorage.getItem() returns promise, so the statement return { fullname: uname} runs before the await statement and once return statement gets executed the function is exited. You can try this instead
AsyncStorage.getItem('#uinfo:name', '').then(res=>{
if(res)
return {
...state,
fullname: uname }
})
Maybe you are using it the wrong way,
actions does not return anything.
You need to map state to props using mapStateToProps config in connect.
const mapStateToProps = ({
userReducer
}) =>{
const {fullname} = userReducer;
return fullname;
}
give this to connect,
export default connect(
mapStateToProps,
mapDispatchToProps
)(YouClass);
you can access your data using, this.props.fullname.
cmponentDidUpdate and componentWillReceiveProps will be fired when reducer will return data.
Note: you have returned data from reducer like this
return { fullname: uname} // never do this, because you will lost all other data/variables other than fullname.
instead do this,
return { ...state,fullname: uname} //this will not do anything to other variables
I'm pretty new in react so this might be a silly question.
I'm working on an app that manage rss feeds, so the structure of my entire app is similar to this one
<div className="App">
<Header />
<Feeds />
</div>
both components have their own reducer and actions.
the problem appears when I'm trying to create a new feed (actually managed in the feeds reducer) from my header component. so I have to access to the state of the feedsReducer from my headerReducer.
I'm not sure how to proceed at this point.
should I access the feeds reducer from the header component? ( this also implies that the feedsReducer needs to know my header actions)
I'll add some code to make the problem clear
index.js
import feedsReducer from './components/Feeds/FeedsReducer';
import headerReducer from './components/Header/HeaderReducer';
const rootReducer = {
feeds:feedsReducer,
header: headerReducer
};
const store = createStore(combineReducers(rootReducer));
Header/Header.js
import { ADD_FEED } from './actions';
class Header extends Component {
state = {
feedUrl: ""
};
addFeed = () => {
axios.post(
'/feeds/add',
{
url: 'myNewRssFeed.com'
})
.then(feed => {
//this is calling the HeaderReducer
this.props.addFeed(feed.data);
})
.catch(err => console.log(err));
}
}
const mapDispatchToProps = dispatch => {
return {
addFeed: (feed) => dispatch({ type: ADD_FEED, payload: { feed } })
};
};
export default connect(null, mapDispatchToProps)(Header);
Header/actions.js
export const ADD_FEED = "ADD_FEED";
HeaderComponent/HeaderReducer.js
const reducer = (state, action) => {
const newState = {
...state
}
switch (action.type) {
case storeActions.ADD_FEED:
// at this point newState.feeds doesn't exist because it's part from the FeedsReducer
newState.feeds = newState.feeds.push(action.payload.feed);
break;
}
return newState;
}
Feeds/FeedsReducer.js
const initialState = {
feeds: []
}
const reducer = (state = initialState, action) => {
const newState = {
...state
}
switch (action.type) {
//this action is commented because was recently moved to the headerComponent/actions.js
/* case storeActions.ADD_FEED:
newState.feeds = newState.feeds.push(action.payload.feed);
break; */
case storeActions.LOAD_FEEDS:
newState.feeds = action.payload.feeds;
break;
}
return newState;
}
Thanks in advice.
I don't really think you need to access reducer in any way. Reducer function will update store based on action it's listenning to.
Here is an example:
import * as constants from 'constantpathhere';
export function feedReducer(state = INITIAL_STATE, action) {
const { type, payload } = action;
switch(type) {
case constants.ADD_FEED: // listen to ADD_FEED action
return {... state, data: payload };
case constants.LOAD_FEEDS: // listen to LOAD_FEEDS
return {...state, loading: true }
...
default:
return state;
}
}
export function headReducer(state = INITIAL_STATE, action) {
const { type, payload } = action;
switch(type) {
case constants.ANY_ACTION: // listen to ADD_FEED action
return {... state, data: payload };
case constants.ANY_OTHER_ACTION_LOADING: // listen to LOAD_FEEDS
return {...state, loading: true }
...
default:
return state;
}
}
//ACTIONS
export function loadFeeds() {
return {
type: constants.LOAD_FEEDS
}
}
export function addFeed(payload) {
return {
type: constants.ADD_FEED,
payload
}
}
export function triggerAnyAction(payload) {
return {
type: constants.ANY_ACTION,
payload
}
}
These actions above may be dispatched from any component, be it Header or Feeds, only reducer(s) listening to that particular action will update the store.
Briefly, you only need to know which action to dispatch where and only reducer listing to that action will do whatever you instructed it to do
I'm developing React/Redux application and I've got problem with getting one particular state from redux store after dispatching an action. I don't have any idea why is that happening, because I haven't experienced such issue with other states. Here is my code:
Reducer
import {SET_CURRENT_USER, SET_LECTURES} from '../actions/actionTypes';
import isEmpty from 'lodash/isEmpty';
const initialState = {
isAuthenticated: false,
user: {},
lectures: []
}
export default (state = initialState, action = {}) => {
switch(action.type) {
case SET_CURRENT_USER:
return {
isAuthenticated: !isEmpty(action.user),
user: action.user
};
case SET_LECTURES:
return {
lectures: action.lectures
}
default: return state;
}
}
Action creator and dispatching action
import { SET_LECTURES } from './actionTypes';
export const setLectures = (lectures) => {
return {
type: SET_LECTURES,
lectures
}
}
export const lecture = (lectures) => {
return dispatch => {
console.log(lectures);
dispatch(setLectures(lectures));
}
}
The problem is with SET_LECTURES action type, in particular lectures property of action object. In the component from which I want to get state lectures, I do mapStateToProps as follows:
const mapStateToProps = function(state) {
return {
notifications: state.notifications,
lectures: state.lectures
}
}
/*
*Code of the class
*/
export default connect(mapStateToProps, null)(AddQuestionForm);
I've skipped code which triggers dispatching action type SET_LECTURES, because it's working fine. I've also used React Developer Tools for tracking states, and there is lectures state. I just can't get this state from my component, when I do console.log(this.props.lectures) from ComponentDidMount(), it shows undefined. Could you explain what am I doing wrong here? I would appreciate any help.
You forgot about dispatch:
export const lectureAction = lectures => dispatch => {
return dispatch => {
dispatch(setLectures(lectures));
}
}
In Component:
import { bindActionCreators } from 'redux';
const mapStateToProps = function(state) {
return {
notifications: state.notifications
}
}
// use map dispatch for actions:
const mapDispatchToProps = dispatch =>
bindActionCreators({
lectures: lectureAction
}, dispatch);
/*
*Code of the class
*/
// connect map dispatch here:
export default connect(mapStateToProps, mapDispatchToProps)(AddQuestionForm);
Now you have an access to this.props.lectures(someParams) function in your Component which dispatch an action.
I've been learning React-Native + Redux and have recently hit a wall while trying to build a relatively small app. My problem is that when I fetch json data from an api (from another one of my apps) using axios, the data comes in the form of an array of objects (e.g. all the door objects). Whenever i try to transfer that array to my component through my reducer, the data gets lost and the array turns up empty every time
My action creator, which uses redux-thunk:
export const doorsFetch = () => {
return (dispatch) => {
axios.get('http://localhost:3000/api/v1/doors')
.then(response => {
dispatch({ type: FETCH_DOORS_SUCCESS, payload: response.data });
})
.catch(error => console.log(error.response.data));
};
};
My DoorsReducer:
const INITIAL_STATE = [];
export default (state = INITIAL_STATE, action) => {
console.log(action.payload);
switch (action.type) {
case FETCH_DOORS_SUCCESS:
return [...state, action.payload];
default:
return state;
}
};
The action.payload console.log turns up what i would expect, an array like this [{object}, {object}]. However in my component the array becomes empty, so i think the problem is in the reducers or in how I map the state to props.
I've also tried the doorReducer this way:
const INITIAL_STATE = {
doors: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_DOORS_SUCCESS:
return { ...state, doors: action.payload };
default:
return state;
}
};
with the same result of an empty array in the component props.
Here is my reducers index:
import { combineReducers } from 'redux';
import OrientationReducer from './OrientationReducer';
import TraitsReducer from './TraitsReducer';
import DoorsReducer from './DoorsReducer';
export default combineReducers({
orientation: OrientationReducer,
traits: TraitsReducer,
doorState: DoorsReducer
});
And finally my component, DoorList:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ListView } from 'react-native';
import { doorsFetch } from '../actions';
import ListItem from './ListItem';
class DoorList extends Component {
componentWillMount() {
this.props.doorsFetch();
this.createDataSource(this.props);
}
componentWillReceiveProps(nextProps) {
this.createDataSource(nextProps);
}
createDataSource({ doors }) {
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.dataSource = ds.cloneWithRows(doors);
}
renderRow(door) {
return <ListItem door={door} />;
}
render() {
console.log(this.props); // for testing
return (
<ListView
enableEmptySections
dataSource={this.dataSource}
renderRow={this.renderRow}
/>
);
}
}
const mapStateToProps = state => {
return {
doors: state.doorState
};
};
export default connect(mapStateToProps, { doorsFetch })(DoorList);
When I console out this.props, I get an object with a doors array that is empty. I can't seem to figure out what happened to the data and how to render it properly in the view. Any insight would be much appreciated. Would happily provide more code/info if requested.
EDIT:
For clarification, this is where i put my console.log statements for debugging purposes:
const INITIAL_STATE = [];
export default (state = INITIAL_STATE, action) => {
console.log(action.payload);
console.log(action.type);
switch (action.type) {
case FETCH_DOORS_SUCCESS:
console.log('fetching doors case triggered');
return [...state, ...action.payload];
default:
console.log('not working!');
return state;
}
};
SOLUTION:
On top of the suggested corrections below, the other glaring issue was that my FETCH_DOORS_SUCCESS constant was improperly imported from another file, and so was undefined in the reducer file. Therefore my switch statement didn't pick it up and it went to the default case. I was missing curly brackets around FETCH_DOORS_SUCCESS.
If action.payload is an array, you also need to put the spread operator on it to properly combine it with the array in state:
Change:
case FETCH_DOORS_SUCCESS:
return [...state, action.payload];
to:
case FETCH_DOORS_SUCCESS:
return [...state, ...action.payload];
It also looks like you're not returning the promise from axios.get():
Change this:
return (dispatch) => {
axios.get('http://localhost:3000/api/v1/doors')
to
return (dispatch) => {
return axios.get('http://localhost:3000/api/v1/doors')