I want to separate modules, so I tried to separate files in the src/store/modules directory.
To merge reducer modules, I use combineReducers() in modules/index.js.
Before separating these modules, modules/index.js file's code was modules/board.js.
Then I added board.js file. I moved code of index.js to board.js. Finally I added combineReducer() in index.js, but somehow it is not working.
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import store from './store';
const rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
src/containers/BoardContainer.js
import React from 'react';
import Board from '../components/Board';
import { connect } from 'react-redux';
import * as boardActions from '../store/modules/board';
class BoardContainer extends React.Component {
componentWillMount() {
this.props.handleReadBoards();
}
render() {
/* ... */
}
}
const mapStateToProps = (state) => {
return {
boardList: state.get('boardList')
};
}
const mapDispatchToProps = (dispatch) => {
return {
handleReadBoards: () => { dispatch(boardActions.readBoardList()) }
};
}
export default connect(mapStateToProps, mapDispatchToProps)(BoardContainer);
src/store/index.js
// redux
import { createStore, applyMiddleware, compose } from 'redux';
import reducers from './modules';
// redux middleware
import thunk from 'redux-thunk';
const store = createStore(reducers,
compose(applyMiddleware(thunk))
);
export default store;
src/store/modules/index.js
import { combineReducers } from 'redux';
import board from './board';
export default combineReducers({
board
});
src/store/modules/board.js
import { createAction, handleActions } from 'redux-actions';
import { Map, List } from 'immutable';
import * as boardApi from '../../lib/api/board';
// Action Types
const READ_BOARD_LIST = 'board/READ_BOARD_LIST';
// Action Creators
export const readBoardList = () => async (dispatch) => {
try {
const boardList = await boardApi.getBoardList();
dispatch({
type: READ_BOARD_LIST,
payload: boardList
});
} catch (err) {
console.log(err);
}
}
// initial state
const initialState = Map({
boardList: List()
})
// reducer
// export default handleActions({
// [READ_BOARD_LIST]: (state, action) => {
// const boardList = state.get('boardList');
// return state.set('boardList', action.payload.data);
// },
// }, initialState);
// reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case READ_BOARD_LIST:
return state.set('boardList', action.payload.data);
default:
return state;
}
}
Your reducer now contains submodules. So that you have to state from which module you want to get the data: state.board.get('boardList').
You can try to setup redux tool to easy visualize your data inside redux.
const mapStateToProps = (state) => {
return {
boardList: state.board.get('boardList')
};
}
Related
Good morning everyone,
I just set up redux on my react project. What i am willing to do is to get data when the component Dashboard is rendered. Data are expenses and incomes variables, which are the sum of all the expenses and incomes.
The problem i get is that my reducer is never triggered. I get no error in my console.
As you can see in actions/dashboard.js, i consoled log when the call to fetchdata is done, and is working correctly. but the call to setBalance does not.
In the reducers/dashboard.js, in the switch case, the default case is always triggered, it's like it does not find the action type.
My Component Dashboard.js
import React, {useEffect} from 'react'
import classes from './Dashboad.module.css'
import { Doughnut } from 'react-chartjs-2'
import {connect} from 'react-redux'
import * as actions from '../../store/actions/index'
const DashBoard = (props) => {
useEffect(() => {
props.onFetchData()
}, [])
// ----------------------------------- Hidden code to not overload the topic -------------------
const mapStateToProps = state => {
return {
incomes: state.incomes,
expenses: state.expenses
}
}
const mapDispatchToProps = dispatch => {
return {
onFetchData: () => dispatch(actions.fetchData),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DashBoard);
actions/index.js :
export {
fetchData
} from './dashboard'
actions/actionsTypes.js :
export const SET_BALANCE = 'SET_BALANCE'
actions/Dashboard.js
import * as actionsTypes from './actionsTypes'
import axios from '../../axios'
export const setBalance = (donnees) => {
console.log('setbalance')
return {
type: actionsTypes.SET_BALANCE,
donnees: donnees
}
}
export const fetchData = () => {
console.log('call fetchdata')
return dispatch => {
axios.get('/movements.json')
.then(response => {
dispatch(setBalance(response.data))
console.log(response.data)
})
.catch(error => console.log(error))
}
}
Reducers/Dashboard.js :
import * as actionsTypes from '../actions/actionsTypes'
const initialState = {
incomes: 0,
expenses: 0
}
const setBalance = (state, action) => {
let fetchedMvts = []
for(let key in action.donnees.data) {
fetchedMvts.push({...action.donnees.data[key]})
}
let sumIncome = 0;
let sumOutcome = 0;
fetchedMvts.forEach(element => {
let parsed = parseInt(element.amount, 10)
if(element.type === "income") {
sumIncome = sumIncome + parsed;
} else {
sumOutcome = sumOutcome + parsed;
}
})
return {...state, incomes: sumIncome, expenses: sumOutcome}
}
const reducer = (state= initialState, action) => {
console.log('TYPE', action.type)
switch (action.type) {
case actionsTypes.SET_BALANCE: return setBalance(state, action);
default:
console.log('problem')
return state;
}
}
export default reducer;
And, in case you need it, the index.js file of my app :
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware} from 'redux'
import * as serviceWorker from './serviceWorker';
import RootReducer from './store/reducers/dashboard'
import thunk from 'redux-thunk'
const store = createStore(RootReducer, applyMiddleware(thunk))
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
Thank you for your help guys
Have a nice day
I have a simple react app that fetches data from a JSON in my local and updates the state. The problem I face is that my reducer is not being called when I dispatch an action. I can see dispatch getting the response from the JSON but state is not updated
I did a console.log on reducer but no luck. I checked the Redux devtools but I don't see the state updated. Here is my code,
store.js
import {createStore, compose, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {routerMiddleware} from 'react-router-redux'
import rootReducer from './combined.reducer'
import {createLogger} from 'redux-logger'
let middlewares = [
thunk
]
const logger = createLogger();
if (process.env.Node_ENV !== "production" && process.env.Node_ENV !== "test") {
middlewares = [
...middlewares,
logger,
require("redux-immutable-state-invariant").default()
]
}
export default function configureStore(history) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(...middlewares, routerMiddleware(history))
);
const store = createStore(
rootReducer(history),
enhancer
)
if (process.env.Node_ENV !== "production" && process.env.Node_ENV !== "test" && module.hot) {
module.hot.accept("./combined.reducer", () => {
const nextReducer = require("./combined.reducer").default
store.replaceReducer(nextReducer)
})
}
return store
combined.reducer.js
import { connectRouter } from 'connected-react-router';
import { combineReducers } from 'redux';
import * as workoutReducer from './features/WorkoutList/Containers/Workout.Reducer'
const createAppReducer = (history) => combineReducers({
router: connectRouter(history),
workoutReducer: workoutReducer
})
const rootReducer = (history) => (state, action) => {
return createAppReducer(history)(state,action)
};
export default rootReducer;
folder structure:
src
features
Workout
Components
Containers
Workout.Actions.js
Workout.Constants.js
Workout.Container.js
Workout.Reducer.js
Workout.js
Workout.Constants.js:
const constants = {
GET_WORKOUT_LIST_REQUEST: "GET_WORKOUT_LIST_REQUEST",
GET_WORKOUT_LIST_SUCCESS: "GET_WORKOUT_LIST_SUCCESS",
GET_WORKOUT_LIST_FAILURE: "GET_WORKOUT_LIST_FAILURE"
}
export default constants
Workout.Container.js:
import { connect } from 'react-redux'
import {withRouter} from 'react-router'
import {bindActionCreators} from 'redux'
import Workout from './Workout'
import * as workoutAction from './Workout.Actions'
export default withRouter(connect(
(state) => ({
workoutListData: state.workoutListData
}),
(dispatch) => ({
workoutActions:bindActionCreators(workoutAction, dispatch)
})
)(Workout))
Workout.Actions.js:
import constants from "./Workout.Constants"
import {getConfigProperty} from "../../../settings"
import {makeGetCall} from "../../../utils/Api"
const WORKOUT_LIST = getConfigProperty("workoutList")
export function getWorkoutList(url = WORKOUT_LIST) {
return (dispatch, getState) => {
dispatch(getWorkoutListRequest(true))
return makeGetCall(url, getState)
.then(res => res.json())
.then(json => {
dispatch(getWorkoutListSuccess(json))
dispatch(getWorkoutListRequest(false))
})
.catch(ex => {
dispatch(getWorkoutListFailure(ex))
dispatch(getWorkoutListRequest(false))
})
}
}
export function getWorkoutListRequest(req) {
return {
type: constants.GET_WORKOUT_LIST_REQUEST,
req
}
}
export function getWorkoutListSuccess(response) {
return {
type: constants.GET_WORKOUT_LIST_SUCCESS,
response
}
}
export function getWorkoutListFailure(exception) {
return {
type: constants.GET_WORKOUT_LIST_FAILURE,
exception
}
}
Workout.Reducer.js
import constants from "./Workout.Constants"
const initialState = {
workoutListData :{}
}
function reducer(state = initialState, action) {
console.log(".................I'm here................")
switch(action.type) {
case constants.GET_WORKOUT_LIST_SUCCESS: {
return Object.assign({}, state, {
workoutListData: action.response.GET_LIST_DATA
})
}
case constants.GET_WORKOUT_LIST_FAILURE: {
return Object.assign({}, state, initialState)
}
default:
return state
}
}
export {initialState}
export default reducer
workout.js:
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {WorkoutListItem} from './../StyledComponents/WorkoutListItem'
import {isEmpty, isEqual} from 'lodash'
import {getConfigProperty} from "../../../settings"
const ROWS_PER_PAGE = getConfigProperty("rowsPerPage")
export default class Workout extends Component {
static get PropTypes() {
return {
workoutListData: PropTypes.array.isRequired
}
}
constructor(props){
super(props)
this.state = {
workoutListData: []
}
}
componentWillMount(){
if(isEmpty(this.props.workoutListData)){
this.props.workoutActions.getWorkoutList()
}
}
loadWorkoutListItem = () => {
return <WorkoutListItem / >
}
render() {
return (
<div>
{this.loadWorkoutListItem()}
</div>)
}
}
The reducer function should have been called. I don't even see the console log printed
I don't think you want
import * as workoutReducer from './features/WorkoutList/Containers/Workout.Reducer'
but just
import workoutReducer from './features/WorkoutList/Containers/Workout.Reducer'
The first is importing the initialState and the reducer as a combined object, as opposed to just importing the reducer.
EDIT: this is in combined.reducer.js
I'm having trouble retrieving data from the Redux store. Redux logger is showing the data but can't seem to get it to render. Below is the code for my container component and my action/reducer:
//COMPONENT:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchGrade } from '../../modules/smiles';
class Main extends Component {
componentDidMount() {
this.props.fetchSmile();
console.log(this.props);
}
render() {
const { smiles } = this.props;
return (
<div>
<h1>This is the Main Component</h1>
</div>
);
}
}
const mapStateToProps = state => {
return { smiles: state.smiles };
};
const mapDispatchToProps = dispatch => {
return {
fetchSmile: params => dispatch(fetchGrade(params))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
//ACTION/REDUCER:
import axios from 'axios';
const ADD_GRADE = 'SMILES/ADD_GRADE';
export function reducer(state = {}, action) {
switch (action.type) {
case ADD_GRADE:
return {
...state,
grade: action.payload
};
default:
return state;
}
}
export const fetchGrade = () => {
return dispatch => {
axios
.get('/api/test')
.then(res => dispatch({ type: ADD_GRADE, payload: res.data }));
};
};
//STORE:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from 'redux-logger';
import reducer from '../modules';
let store;
export function configureStore(state: {}) {
if (!store) {
store = createStore(
reducer,
state,
composeWithDevTools(applyMiddleware(logger, thunk))
);
}
return store;
}
//INDEX.JS:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';
import { configureStore } from './store';
window.store = configureStore();
render(
<Provider store={window.store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
I really don't know if this is complicated or an easy fix. I feel like I'm doing everything right but no luck.
You named your reducer, reducer:
export function reducer(state = {}, action) {
Seems that you forgot to access it from your reducer object. it should be something like this:
const mapStateToProps = state => {
return { smiles: state.reducer.smiles };
};
I'm trying hard to wire redux store in a react-native app but seems like I'm missing something. Any help will be appreciated.
action.js
export const getdata = (data) => {
return (dispatch, getState) => {
dispatch({
type: "GET_DATA",
data
});
};
};
reducer/dataReducer.js
export default (state = {}, action) => {
switch (action.type) {
case GET_DATA:
return { ...state, response: action.data };
default:
return state;
}
};
reducer/index.js
import { combineReducers } from 'redux';
import dataReducer from './dataReducer';
//other imports
export default combineReducers({
data: dataReducer,
//other reducers
});
store/configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './../reducer;
export default function configure(initialState = {}) {
const store = createStore(reducer, initialState, compose(
applyMiddleware(thunk),
window.devToolsExtension ? window.devToolsExtension() : f => f
));
return store;
}
main.js (where I dispatch action)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Routes from './Routes';
import configureStore from './store/configureStore';
import { getdata} from './actions';
const store = configureStore();
store.subscribe(() => {
console.log('New state', store.getState); //doesn't update at all
});
class Main extends Component {
componentDidMount() {
store.dispatch(getdata('abc')); //calling action creator
}
render() {
return (
<Provider store={store}>
<Routes />
</Provider>
);
}
}
export default Main;
I also tried wiring Chrome extension to see redux store updates, but no luck there. It always says no store found. How can I get this working?
store can be accessed in the a child class inside the Routes Component by react-redux connect
but here no store in the class but I think You can do the following
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Routes from './Routes';
import configureStore from './store/configureStore';
import { getdata} from './actions';
const store = configureStore();
store.subscribe(() => {
console.log('New state', store.getState()); //getState() is method
});
store.dispatch(getdata('abc'));
class Main extends Component {
render() {
return (
<Provider store={store}>
<Routes />
</Provider>
);
}
}
export default Main;
You want to dispatch your actions from your container components (aka. smart components, the ones connected to the Redux store). The container components can define props in mapDispatchToProps that let them dispatch actions. I don't have your code, so I'm just gonna assume that your Routes component is the container component that you are connecting to the Redux store. Try something like:
class Routes extends Component {
....
componentDidMount() {
this.props.retrieveData();
}
...
}
const mapStateToProps = state => {
...
};
const mapDispatchToProps = dispatch => {
return {
retrieveData: () => dispatch(getdata('abc'));
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Routes);
I'm using redux and NetInfo to manager connection detection during startup and any actions where connection is important.
import createOneShotMiddleware from 'redux-middleware-oneshot';
import { NetInfo } from 'react-native';
import { checkConnection } from '../actions/networkActions';
export const middleware = createOneShotMiddleware((dispatch) => {
const handle = (isConnected) => dispatch(checkConnection(isConnected));
NetInfo.isConnected.fetch().done(handle);
NetInfo.isConnected.addEventListener('change', handle);
});
Actions
import * as types from './actionTypes';
export function checkConnection(isConnected) {
return {
type: types.CHECK_CONNECTION,
isConnected: isConnected
};
}
Reducers
import { CHECK_CONNECTION } from '../actions/actionTypes';
const initialState = {
isConnected: false,
};
export default function network(state = initialState, action = {}) {
switch (action.type) {
case CHECK_CONNECTION:
return Object.assign({}, state, {isConnected: action.isConnected})
default:
return state;
}
}
App.js
import React, { Component } from 'react-native';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { middleware as netInfo } from './Middleware/redux-middleware-react-native-netinfo';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger'
import * as reducers from './reducers';
import NavigationScreen from './Containers/NavigationScreen';
const logger = createLogger()
const createStoreWithMiddleware = applyMiddleware(thunk, logger, netInfo)(createStore)
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer)
export default class App extends Component {
render() {
return (
<Provider store={store}>
<NavigationScreen />
</Provider>
);
}
}
It's not work and don't update the state, any suggestions?
This is old but for anyone that arrives here in the future this is how i have done it i tend to do this on my actions creators:
export function networkCheck(){
return (dispatch) => {
const dispatchNetworkState = (isConnected) => dispatch({
type: types.NETWORK_STATE,
state: isConnected
})
const handle = () => NetInfo.isConnected.fetch().done(dispatchNetworkState)
NetInfo.isConnected.addEventListener('change', handle);
}
}
the isue on the code before is that is not following the chain of eventlistener -> testNetwork -> dispatch result