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.
Related
I have 2 reducers (count and search) which I have combined. When the search component dispatches both reducers are called.
Is this expected behavior that all reducers are called?
Or, should only the most specific reducer (i.e. search) be called? What must I do to get only the most specific reducer to be called?
Here are the search, count and combined reducers
const initialState = {url:'...'};
function search(state = initialState, action) {
if (action.type === 'SEARCH') {
...
return state;
}
return state;
}
export default search;
.
const initialState = 0;
function count(state = initialState, action) {
if (action.type === 'INCREMENT') {
return state + 1;
}
if (action.type === 'DECREMENT') {
return state - 1;
}
return state;
}
export default count;
.
import CountReducer from './CountReducer.js';
import SearchReducer from './ApiReducer.js';
import { combineReducers } from 'redux'
const reduce = combineReducers({
count: CountReducer,
search: SearchReducer
});
export default reduce;
The search component looks like this
import React, { Component } from 'react';
import { onSearch as onSearchAction } from '../store/actions/Actions.js';
import { connect } from 'react-redux';
class Search extends Component {
render() {
return (
<>
....
</>
);
}
}
const mapStateToProps = (state) => {
return { url: state.url };
}
const mapDispatchToProps = (dispatch, ownProps) => {
return { onSearch: () => dispatch(onSearchAction()) };
};
const ConnectedSearch = connect(mapStateToProps, mapDispatchToProps)(Search);
export default ConnectedSearch;
And here the search action
export function onSearch() {
return {
type: "SEARCH",
url: '...'
}
}
Is this expected behavior that all reducers are called?
Yes.
What must I do to get only the most specific reducer to be called?
Well, you can write a custom reducer to do that, but i'm not sure what the benefit is. Presumably you would write code that says "if it's this action, or this action, or this action then call reducer 1; otherwise call reducer 2". But checking the action types is what reducer 1 already does so you're just duplicating the logic. Why not just call both reducers, and let them handle or ignore whatever they care or don't care about?
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;
}
}
/reducers/index.js
import * as types from '../actions/ActionTypes';
var initialState = {
categoryName: 'default name redux'
};
function re_modifyCategoryName(state=initialState, action) {
switch(action.type) {
case types.MODIFY_CATEGORY_NAME :
return Object.assign({}, state, {
categoryName: action.categoryNewName
});
default:
return state;
}
}
export {re_modifyCategoryName}
I definitely check state of redux store is updated by Redux-dev-tool and redux-logger,
but mapStateToProps is not being called.
PresentationalComponent.js
...
const mapStateToProps = (state) => {
console.log("inside category name", state.categoryName);
return {categoryName: state.categoryName}
};
export default connect(mapStateToProps)(AnswerEachCategory);
...
When the page is rendered first time, as you can see mapStateToProps, "inside category name", state.categoryName is shown in console and presentational component correctly get props.categoryName from initialState of redux store. However, why the change in redux store didn't trigger mapStateToProps??
Is this problem related with immutability of state of redux store?
Container.js
...
const mapDispatchToProps = (dispatch) => ({
onModifyCategoryName: (categoryNewName) => {
dispatch(actions.modifyCategoryName(categoryNewName))
}
})
export default connect(null, mapDispatchToProps)(ModifyCategoryNameModal)
...
I am trying to pass the state to as props through mapStateToProps(), and even though the state change is being reflected in the state tree (redux tool inspector) and the 'data' being passed down as the action payload is successfully logging in the reducer.js, the state is not being passed down as props in my container.
my reducer.js:
import { fromJS } from 'immutable'
import { GOT_INDUSTRY_DATA } from './constants.js';
const initialState = fromJS({
data: []
})
function industryDataReducer(state = initialState, action){
switch (action.type) {
case GOT_INDUSTRY_DATA: {
const { data } = action
console.log('reducer data',data)
return state.set('data', fromJS(data))
}
default:
return state
}
}
export default industryDataReducer
in my container:
export class IndustryPage extends React.PureComponent {
render(){
const { data } = this.props
console.log(data)
return{
...
}
}
}
function mapStateToProps({data}) {
return {
data
}
}
const withConnect = connect(mapStateToProps, mapDispatchToProps)
const withSaga = injectSaga({ key: 'industry', saga })
const withReducer = injectReducer({ key: 'industry', reducer })
export default compose (
withStyles(styles),
withSaga,
withReducer,
withConnect
)(IndustryPage);
I've just started implementing Redux in a React application, and it's the first time i try to, so please bear with me.
My problem is that i can't access the data in my component this this.props.questions
I have a simple action which is supposed to async fetch some data
export function fetchQuestions(url) {
const request = axios.get('url');
return (dispatch) => {
request.then(({data}) => {
dispatch({ type: 'FETCH_QUESTIONS', payload: data });
console.log(data);
});
};
}
Which is picked up my reducer questions_reducer
export default function(state = [], action) {
switch(action.type) {
case 'FETCH_QUESTIONS':
console.log('Getting here');
return state.concat([action.payload.data]);
console.log('But not here');
}
return state;
}
My index reducer looks like this:
import { combineReducers } from 'redux';
import fetchQuestions from './question_reducer';
const rootReducer = combineReducers({
questions: fetchQuestions
});
export default rootReducer;
I pass it to my store where i apply the thunk middleware and finally into <Provider store={store}> which wraps my app, but the prop just returns undefined in my React component
configureStore:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
I don't know if the console.log is to be trusted but it logs from my questions_reducer before the data is returned from the dispatch in my action
EDIT (Component)
class QuestionsRoute extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.props.fetch('someUrl);
setTimeout(function(){ console.log(this.props.questions) },
1500);
}
render() {
{console.log(this.props.questions)}
return (
<div>
<1>Hello</1>
{this.props.questions !== undefined ?
<p>We like props</p>: <p>or not</p>
}
</div>
);
}
};
const mapStateToProps = (state) => {
return {
questions: state.questions,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetch: () => dispatch(fetchQuestions())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(QuestionsRoute);
In your reducer
export default function(state = [], action) {
switch(action.type) {
case 'FETCH_QUESTIONS':
return state.concat([action.payload.data]);
}
return state;
}
You should probably instead have return state.concat([action.payload]);
Since from dispatch({ type: 'FETCH_QUESTIONS', payload: data }); we see that payload is data, it doesn't contain it.
Update: I'd recommend setting up redux-devtools / redux-devtools-extension / react-native-debugger so you can visually see your actions and store state live - makes things like this a lot easier to debug!