I am trying to develop a simple image list component with react-redux stack.
This are my actions, reducers, saga and component root definitions -
// Actions
export const getImageListData = () => ({
type: IMAGE_LIST_GET_DATA
});
export const getImageListDataSuccess = (data) => {
console.log("ACTION::SUCCESS", data);
return ({
type: IMAGE_LIST_GET_DATA_SUCCESS,
payload: data
});
};
// Reducers
export default (state = INIT_STATE, action) => {
console.log("REDUCER::", state, action);
switch (action.type) {
case IMAGE_LIST_GET_DATA: return { ...state, isLoading: true };
case IMAGE_LIST_GET_DATA_SUCCESS: return { ...state, items: action.payload.data, isLoading: false };
default: return { ...state };
}
}
// Sagas
import imagesData from "Data/images.json";
function* loadImages() {
try {
const response = yield call(loadImagesAsync);
console.log("SAGA:: ", response);
yield put(getImageListDataSuccess(response));
} catch (error) {
console.log(error);
}
}
const loadImagesAsync = async () => {
const contacts = imagesData;
return await new Promise((success, fail) => {
setTimeout(() => {
success(contacts);
}, 2000);
}).then(response => response).catch(error => error);
};
export function* watchGetImages() {
console.log("ACTION::INIT", IMAGE_LIST_GET_DATA);
yield takeEvery(IMAGE_LIST_GET_DATA, loadImages);
}
export default function* rootSaga() {
yield all([
fork(watchGetImages)
]);
}
Now in the component I am calling - getImageListData action
and with this mapStateToProps and connect provider -
const mapStateToProps = ({ ImageList }) => {
const {items} = ImageList;
return {items};
};
export default connect(
mapStateToProps,
{
getImageListData
}
)(ImageListLayout);
I am mapping the image list response to the component props.
My component definition is as follows -
class ImageListLayout extends Component {
constructor(props) {
super(props);
this.state = {
displayMode: "imagelist",
pageSizes: [8, 12, 24],
selectedPageSize: 8,
categories: [
{label:'Cakes',value:'Cakes',key:0},
{label:'Cupcakes',value:'Cupcakes',key:1},
{label:'Desserts',value:'Desserts',key:2},
],
orderOptions:[
{column: "title",label: "Product Name"},
{column: "category",label: "Category"},
{column: "status",label: "Status"}
],
selectedOrderOption: {column: "title",label: "Product Name"},
dropdownSplitOpen: false,
modalOpen: false,
currentPage: 1,
items: [],
totalItemCount: 0,
totalPage: 1,
search: "",
selectedItems: [],
lastChecked: null,
displayOptionsIsOpen: false,
isLoading:false
};
}
componentDidMount() {
this.dataListRender();
}
dataListRender() {
this.props.getImageListData();
}
render() {
...
}
}
Now in my component I am able to correctly access this.props.items obtained from reducer with action IMAGE_LIST_GET_DATA_SUCCESS, but I also want to update some of the state variables like isLoading, currentPage, totalItemCount, totalPage and since these belong to this component itself and not their parents I do not want to map them to the props but want to update the state of the component and trigger a re-render.
Can someone please tell me what should I be doing to fix this or am i missing anything else here ?
In your current setup I see no reason for you to have isLoading, etc. in the state. You should just map it to props:
const mapStateToProps = ({ ImageList, isLoading }) => {
const {items} = ImageList;
return {items, isLoading};
};
I don't get why you say "and since these belong to this component itself and not their parents I do not want to map them to the props but want to update the state of the component and trigger a re-render." what do parents have to do with anything here?
Related
I am trying to display a mock JSON data using react-sagas to display the dummy data on my my main component. I followed all the steps related related to setting up the sagas and store. but my main components doesn't show any data in return I keep getting an error pointing which points to my selectors. Uncaught TypeError: main.get is not a function
1. Main Component
class Main extends React.Component {
static propTypes = {
handlePOConfigRules: PropTypes.func,
rules: PropTypes.object
};
constructor(props, context) {
super(props, context);
this.state = {
priorityCardList: [
{
title: "Rule Priority 1 ",
id: "1",
rule_code: "NW"
},
{
title: "Rule Priority 2",
id: "2",
rule_code: "3D"
},
]
};
this.props.handlePOConfigRules();
}
componentDidMount() {
console.log("1. Mount - Props", this.props);
console.log("2. Mount - State", this.state);
}
render() {
const { priorityCardList } = this.state;
const { rules } = this.props;
console.log("Main render props --", rules);
return (
<>
{/* Loop through the cardlist for rule priority */}
{priorityCardList.map(element =>
<RuleDetailCard title={element.title}
id={element.id}
rule_code={element.rule_code} />
)}
</>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
// dispatching plain actions
handlePOConfigRules: () => dispatch(actions.getRuleDetails())
}
}
const mapStatetoProps = () => createStructuredSelector({
rules: selectors.getRuleDetails()
});
export default connect(mapStatetoProps, mapDispatchToProps)(Main);
2. Actions :
export function getRuleDetails(){
return {
type: 'GET_PO_CONFIG_START'
}
}
export function getRuleDetailsSuccess({rules}){
return {
type: 'GET_PO_CONFIG_SUCCESS',
payload: {rules}
}
}
3. Reducer :
export const initialState = fromJS({
//inital setup of reducers
rules: []
});
function main(state = initialState, action) {
switch (action.type) {
case 'GET_PO_CONFIG_START':
return { ...state, rules: initialState.rules};
case 'GET_PO_CONFIG_SUCCESS':
return { ...state, rules: action.payload}
case 'GET_PO_CONFIG_ERROR':
return { ...state, rules: initialState.rules}
default:
return state
}
}
export default main;
4. Selectors
import { createSelector } from 'reselect';
const mainState = (state) => state?.['main'];
export const getRuleDetails = () => createSelector(
mainState,
(main) =>{
const data = main.get('rules') ? main.get('rules').toJS(): [];
return data;
}
);
5. Sagas
export function* getPOConfigData(action) {
try {
const { payload } = action;
// Api Call from services
const results = yield call(getAllPOConfigs, payload);
//Selectors
const ruleList = yield select(selectors.getRuleDetails());
//On success set the payload value
yield put(actions.getRuleDetailsSuccess({rules: results}));
} catch (e) {
yield put({ type: 'GET_PO_CONFIG_ERROR', message: e.message });
}
}
export function* POConfigWatcher() {
//debugger;
yield takeLatest('GET_PO_CONFIG_START', getPOConfigData);
}
export default [
fork(POConfigWatcher)
]
In selectors, const data = main.get('rules') ? main.get('rules').toJS(): []; is where i am getting error while accessing this.props.rules in my Main component. Can anyone guide me what is missing here ???
I converted this application
https://codesandbox.io/s/3y77o7vnkp <--Please check this link
into my react-native app and it works perfect. Since I implemented redux and redux-thunk I have a problem with fetching data.
There is a problem. I converted normal functions from this upper link to actions and reducers in react-native. For Example
handleSelect = itemValue => {
this.setState(
{
...this.state,
base: itemValue,
result: null,
},
this.calculate
);
};
TO
actiontypes.js
export const HANDLE_FIRST_SELECT = 'HANDLE_FIRST_SELECT';
action.js
export const handleFirstSelect = itemValue => {
return {
type: actionTypes.HANDLE_FIRST_SELECT,
itemValue: itemValue,
};
};
and reducer.js
const initialState = {
currencies: ['USD', 'AUD', 'SGD', 'PHP', 'EUR', 'PLN', 'GBP'],
base: 'EUR',
amount: '',
convertTo: 'PLN',
result: '',
date: '',
error: null,
loading: false,
};
const exchangeCurrencies = (state = initialState, action) => {
switch (action.type) {
case actionTypes.HANDLE_FIRST_SELECT:
return {
...state,
base: action.itemValue,
result: null,
};
...
Next step I used mapStateToProps and mapDispatchToProps in my component like this
const mapDispatchToProps = dispatch => {
return {
handleFirstSelect: itemValue =>
dispatch(exchangeCurriencesActions.handleFirstSelect(itemValue)),
...
const mapStateToProps = (state) => {
return {
base: state.base,
amount: state.amount,
convertTo: state.convertTo,
result: state.result,
date: state.date,
};
};
And I'm using now this.props
<PickerComponent
selectedValue={this.props.base}
onValueChange={this.props.handleFirstSelect}
/>
Until then, everything works ok. Now when I download data in this way with react-redux and redux-thunk (action.js) it stops working
export const fetchDataSuccess = data => {
return {
type: actionTypes.FETCH_DATA_SUCCESS,
data: data,
};
};
export const fetchDataFail = error => {
return {
type: actionTypes.FETCH_DATA_FAIL,
error: error,
};
};
export const fetchData = () => {
return dispatch => {
fetch(`https://api.exchangeratesapi.io/latest?base=${this.props.base}`)
.then(res => res.json())
.then(
data => dispatch(fetchDataSuccess(data.rates)),
e => dispatch(fetchDataFail(e)),
);
};
};
next reducer.js
...
case actionTypes.FETCH_DATA_BEGIN:
return {
...state,
loading: true,
error: null,
};
case actionTypes.FETCH_DATA_SUCCESS:
console.log('data', action.data);
return {
...state,
date: action.data.date,
result: action.data.rates,
loading: false,
};
case actionTypes.FETCH_DATA_FAIL:
console.log('data', action.error);
return {
...state,
loading: false,
error: action.error,
};
...
In the next step added function fetchData into mapDispatchToProps and call this in componentDidMount like that
componentDidMount() {
if (this.props.amount === isNaN) {
return;
} else {
try {
this.props.fetchData();
} catch (e) {
console.log('error', e);
}
}
}
and finnaly I add calculations for currencies in mapStateToProps. I change result like that
result: (state.result[state.convertTo] * state.amount).toFixed(4),
and also I added applymiddleware into the store.
AND FINNALY THERE IS A ERROR
import React from 'react';
import HomeContentContainer from '../../containers/HomeContentContainer/HomeContentContainer';
class HomeScreen extends React.Component {
render() {
return <HomeContentContainer />;
}
}
export default HomeScreen;
Anyone know how to resolve this problem? Where and what should I change the code?
Following up on the comments to the question, please do review those...
In mapStateToProps protect your access to state.result, like this perhaps:
result: state.result ? (state.result[state.convertTo] * state.amount).toFixed(4) : null
, where null could also be 0 or whatever, your call. Your mapStateToProps is indeed getting called (at least) once before the API request is completed, so it needs to handle your initial state of state.result.
I find it confusing that the components property result and the state.result are different things, but this is just my opinion about naming.
So inside of my uncontrolled PossibleMatches component, I know from the way React works, the initial rendering phase will occur with empty prop values (if those prop values rely on external application state (mapStateToProps)) regardless of whether or not I have a componentDidMount lifecycle method or constructor setup. In response to this, I've setup a promise inside of the componentDidMount so that when I dispatch prop functions [defaultPieces, arrangePieces], I can have the UI render an ActivityIndicator to indicate something is currently fetching. The problem is, I cannot seem to get the mapStateToProps function to understand the state when I call mapStateToProps from within the success phase of the promise. Here it is:
class PossibleMatches extends Component {
constructor(props){
super(props);
}
componentDidMount(props){
return new Promise((resolve, reject) => {
let state;
let {defaultPieces, arrangePieces, isFetching} = this.props;
let makeClothesAppear = function(){
defaultPieces();
arrangePieces();
isFetching = true;
}
resolve(makeClothesAppear());
}).then(function(state){
mapStateToProps(state);
this.props.isFetched = true
this.props.isFetching = false;
}).catch((error) => {
console.log('FetchClothesError: ', error);
})
}
}
How the UI would make a decision on what to display:
renderDecision(){
const {UpperComponents, LowerComponents} = this.props;
const {currentUpperComponent, currentLowerComponent} = this.state.currentComponent.whichPiece;
const {LowerComponentEnabled, UpperComponentEnabled} = this.state;
if (this.props.isFetching){
return (<div className='activityLoader'>
<ActivityIndicator number={3} duration={200} activeColor="#fff" borderWidth={2} borderColor="50%" diameter={20}/>
</div>);
} else if (this.props.isFetched){
return (<div className = "PossibleMatches_Container">
<i className = 'captureOutfit' onClick = {this.snapshotMatch}></i>
{UpperComponents.map((component) => {
return (<UpperComponent key={component.createdAt} id={component.id}
switchComponent={this.switchFocus}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece = {(LowerComponentEnabled) => {if (LowerComponentEnabled === false){this.setState({LowerComponentEnabled: true})}else{return;} this.setState({currentLowerComponent: this.props.suggestedBottoms[0]})}}
isLowerComponentEnabled={LowerComponentEnabled}
ref={this.residingUpperComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
{LowerComponents.map((component) => {
return (<LowerComponent key={component.createdAt} id={component.id}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece={(UpperComponentEnabled) => {if (UpperComponentEnabled === false){this.setState({UpperComponentEnabled: true})}else{return;} this.setState({currentUpperComponent: this.props.suggestedTops[0]})}}
switchComponent={this.switchFocus}
isUpperComponentEnabled={UpperComponentEnabled}
ref={this.residingLowerComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
</div>)
}
}
render(){
return(
<div className = 'GorClothingContainer'>
{/*<Wardrobe upperComponent={this.state.currentComponent.whichPiece.currentUpperComponent} lowerComponent={this.state.currentComponent.whichPiece.currentLowerComponent} enableCapture={(snapshot) => this.snapshotMatch = snapshot} />*/}
{this.renderDecision()}
</div>
);
}
My PossibleMatches Reducer
import {INITIAL_PIECES, GET_ANCILLARY_PIECES, ORGANIZE_PIECES, SET_CONTEMPLATED_PIECE} from '../actions/types';
const initialState = {
UpperComponents: [],
LowerComponents: [],
contemplated_piece: null,
extraTops: [],
extraBottoms: [],
standaloneTops: [],
standaloneBottoms: [],
suggestedTops: [],
suggestedBottoms: []
}
export default function(state = initialState, action){
switch(action.type){
case INITIAL_PIECES:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece},
{extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case GET_ANCILLARY_PIECES:
return Object.assign({}, state, {extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case ORGANIZE_PIECES:
return Object.assign({}, state, {UpperComponents: action.payload.UpperComponents},
{LowerComponents: action.payload.LowerComponents})
case SET_CONTEMPLATED_PIECE:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece})
default:
return state;
}
}
My combineReducers segment
import {combineReducers} from 'redux';
const allReducers = combineReducers({
Playlist: PlaylistReducer,
eventOptions: eventTicketReducer,
possibleMatches: PossibleMatchesReducer,
Intro: combineForms({
basicUserInfo: BasicUserInfoState,
GenderInfo: GenderInfoState,
ContactInfo: ContactInfoState
}, 'Intro'),
routing: routerReducer,
form: formReducer
});
Prop Values:
PossibleMatches.defaultProps = {
isFetching: true,
isFetched: false
}
My mapStateToProps function
function mapStateToProps(state){
return {UpperComponents: state.possibleMatches.UpperComponents,
LowerComponents: state.possibleMatches.LowerComponents,
contemplatedPiece: state.possibleMatches.contemplated_piece,
extraTops: state.possibleMatches.extraTops,
extraBottoms: state.possibleMatches.extraBottoms,
standaloneTops: state.possibleMatches.standaloneTops,
standaloneBottoms: state.possibleMatches.standaloneBottoms,
suggestedTops: state.possibleMatches.suggestedTops,
suggestedBottoms: state.possibleMatches.suggestedBottoms}
}
function mapDispatchToProps(dispatch){
return {
defaultPieces: () => {
dispatch(defaultPieces())
},
arrangePieces: () => {
dispatch(arrangePieces())
},
getCorrespondingPieces: () => {
dispatch(getCorrespondingPieces())
},
setEvaluatedPiece: () => {
dispatch(setEvaluatedPiece())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PossibleMatches)
My Question is: What exactly is wrong with the way that I've implemented the promise. With the reducers and the redux actions setup correctly(I know because I've logged the fetched items to the console from the redux actions file), how can I properly populate the prop values in mapStateToProps. Currently the error is:
Im using React 16.4.0
A simple redux use case would seem as follows
possibleMatches.jsx (Component file)
class PossibleMatches extends React.Component {
state = {
isFetching: false
}
componentDidMount() {
this.setState({isFetching: true})
fetchingSomethingFromServer()
.then(resp => {
this.setState({isFetching: false})
this.props.UpdateRedux(resp)
});
}
render() {
const { isFetching } = this.state;
const { data } = this.props;
return (
isFetching ? <div>loading...</div> : <div>{data}</div>
)
}
}
export default connect(state => ({ data: state.possibleMatches.data }), {UpdateRedux})
actions.js (action creator file)
Use this action to update any data into redux
export const UpdateRedux = (data) => {type: 'UPDATE_REDUX', payload: data}
reducers.js
This is the file that holds the redux state
const defaultState = {
data: null
}
export default (state = defaultState, action) => {
switch(action.type) {
case 'UPDATE_REDUX':
return {data: action.payload};
default:
return state
}
}
In your combine reducers import this reducer and assign it as follows
import possibleMatches from 'reducers.js';
combineReducers({ possibleMatches });
I'm developing my first React Native app and it is the first time I'm using redux and redux saga. So I've built a Flatlist to have infinite scroll with a API endpoint tha returns posts (10 per page). But I don't know how to use the reducers to return the posts, control the loading indicator and keep track of the page number in the store, using redux saga.
My code is the following:
Home.js
this.state = {
page: 1,
totalPages: 10,
loading: false,
}
componentDidMount() {
this.loadMorePosts();
}
loadMorePosts = () => {
this.setState(() => { loading: true });
this.setState(() => { page: this.state.page++ });
this.props.loadPosts(this.state.page);
}
<AnimatedFlatList
...
onEndReached={this.loadMorePosts}
onEndReachedThreshold={0.2}
/>
const mapStateToProps = state => ({
posts: state.posts,
});
Posts Action
export function loadPosts(page){
return {
type: Types.FETCH_POSTS,
payload: { page }
};
}
Posts saga
export function* fetchPosts(action) {
const response = yield call(api.get, `/posts/${action.payload.page}`);
yield put({ type: Types.LOAD_POSTS, payload: { posts: response.data } });
}
Posts Reducer
export default function posts(state = initialState, action) {
switch(action.type) {
case Types.LOAD_POSTS:
return [ ...state, ...action.payload.posts ];
default:
return state;
}
}
With this I can fetch the posts and load into the Flatlist, but if I change screens I lose track of the actual page number, that will be set to 0 again in the Home.js constructor. And there is no visual feedback since the loading state is not defined with the mapStateToProps function...
Can anyone help me solve this problem?
Expanding on a comment: (Not tested code but principle is there).
Saga
export function* fetchPosts(action) {
try {
yield put({ type: Types.LOAD_POSTS_START, payload: { page: action.payload.page } });
const response = yield call(api.get, `/posts/${action.payload.page}`);
yield put({ type: Types.LOAD_POSTS, payload: { posts: response.data } });
}
catch {
//perhaps roll back page count?
yield put({ type: Types.LOAD_POSTS_END, payload: { } });
}
}
Reducer
const initialState = {
isLoading: false,
currentPage: 0,
posts: []
}
export default function posts(state = initialState, action) {
switch(action.type) {
case Types.LOAD_POSTS_START:
return {
...state,
currentPage: action.payload.page,
isLoading: true
};
case Types.LOAD_POSTS_END:
return {
...state,
isLoading: false
};
case Types.LOAD_POSTS:
return {
...state,
isLoading: false,
posts: [ ...state.posts, ...action.payload.posts ]
};
default:
return state;
}
}
Then in your component connect to this state rather than have it stored in the components state object
Make a saga/task that just does a fetch and returns a promise like this:
const fetchAction = (input, init) => {
let resolve;
const promise = new Promise(resolveArg => resolve = resolveArg)
return { type:'FETCH_ACTION', input, init, promise, resolve }
}
function fetchActionWorker({ input, init, resolve}) {
const res = yield call(fetch, input, init);
resolve(res);
}
function* fetchActionWatcher() {
yield takeEvery('FETCH_ACTION', fetchWorker);
}
Then use it like this:
class List extends Component {
render() {
return <Button title="fetch" onPress={this.doFetch} />
}
doFetch = async () => {
const res = await dispatch(fetchAction('url', { method:'GET' })).promise;
}
}
Calling the fetch action gives you a promise right away.
I'm fairly new to React/Redux. The redux Chrome devtools tell me that I'm successfully creating and modifying state. However, while the console log 'dah state' runs and satisfactorily tells me that my state has the correct information in it, I'm not mapping to props at that point.
I'm not entirely sure if my reducer is made correctly, but I suspect that it is because I'm creating new state, even though it doesn't map to props.
I'm also fairly sure that mapStateToProps, while it runs, is not triggering a rerender
Here is my relevant container
import React, { Component, PropTypes } from 'react';
import TopicsGrid from '../components/TopicsGrid.jsx';
import { connect } from 'react-redux';
import { fetchTopics } from '../actions/topics';
import Main from '../components/Main.jsx';
class AboutContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
topics: [{
title: '',
description: '',
link: '',
src: '',
message: '',
selected: false,
_id: ''
}]
}
}
onChange = (action) => {
this.props.dispatch(action);
}
componentDidMount() {
fetchTopics();
}
componentWillReceiveProps(nextProps) {
console.log('nextProps', nextProps)
this.setState({
topics: nextProps.topics
})
}
render() {
console.log('PROPS', this.props)
return (
<div className="container">
<TopicsGrid
topics={this.state.topics}
onChange={this.onChange}/>
</div>
);
}
};
AboutContainer.propTypes = {
dispatch: PropTypes.func.isRequired,
topics: PropTypes.array.isRequired
};
AboutContainer.defaultProps = {
state: {
topics: [{
title: '',
description: '',
link: '',
src: '',
message: '',
selected: false,
_id: ''
}]
}
};
const mapDispatchToProps = (dispatch) => {
return {
dispatch: dispatch
}
}
const mapStateToProps = (state) => {
console.log('dah state', state)
return Object.assign({}, state, {
topics: state.topics.topics
})
}
export default connect(mapStateToProps, mapDispatchToProps)(AboutContainer);
Here is my reducer
import * as types from '../constants/action-types';
import * as SectionNames from '../constants/section-names';
const initialState = {
topics: []
}
export default function about(state = initialState, action) {
if (action.section !== SectionNames.TOPICS) {
return state;
}
let mods = {};
switch (action.type) {
case types.FETCH_TOPICS_SUCCESS:
mods = {
topics: action.topics
}
// return Object.assign({}, state, {
// topics: action.topics
// });
break;
case types.FETCH_TOPICS_ERROR:
mods = {
topics: action.topics
}
// return Object.assign({}, state, {
// topics: action.topics
// });
break;
case types.TOPIC_SELECTED:
console.log('selected')
//topic can be selected or unselected
//only one topic can be selected at once.
mods = {
topics: action.topics
}
mods.topics[action.index].selected = true;
return Object.assign({}, state, mods);
break;
case types.TOPIC_UNSELECTED:
//topic can be selected or unselected
//only one topic can be selected at once.
mods = {
topics: action.topics
}
mods.topics[action.index].selected = false
break;
default:
return state;
}
return Object.assign({}, state, mods);
}
Since you're using mapStateToProps you can use props directly instead of passing them into the component state.
render() {
const { topics } = this.props
return (
<div className="container">
<TopicsGrid
topics={topics}
onChange={this.onChange}
/>
</div>
);
}
#connect(state => ({
data: state.module.data
}), {actoionCreators});
use this at the top of your class it will decorate your class and map states to props
in your module you have to switch on action.type, in each case you should return an object with your custom changes in state for example in LOAD_SUCCESS you have to return an object like this :
return {
...state,
loading:false,
loaded:true,
data:action.data
}
so redux knows that when loadSuccess dispatched the state is gonna change with loading to false and ....
notice that reduce should return state when an unknown action dispathed so in your default case you should return state