reactjs redux Actions must be plain objects - javascript

I have a problem "Actions must be plain objects. Use custom middleware for async actions."
I'm using reactjs with this boilerplate (https://github.com/react-boilerplate/react-boilerplate/)
I waste 1 day to fix this problem, but no result. I was trying move fetch request to action (without saga) and result the same.
My component:
...
import { compose } from 'redux';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import { successFetching } from './actions';
import reducer from './reducers';
import saga from './saga';
class NewsListPage extends React.PureComponent {
componentDidMount() {
const { dispatch } = this.props
dispatch(saga())
}
...
};
NewsListPage.propTypes = {
isFetching: PropTypes.bool.isRequired,
isSuccess: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
}
const selector = (state) => state;
const mapStateToProps = createSelector(
selector,
(isFetching, isSuccess, items) => ({ isFetching, isSuccess,items })
);
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withReducer = injectReducer({ key: 'NewsList', reducer });
const withSaga = injectSaga({ key: 'NewsList', saga });
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
withReducer,
withSaga,
withConnect
)(NewsListPage);
My actions:
export const NEWS_FETCH_LOADING = 'NEWS_FETCH_LOADING';
export const NEWS_FETCH_FAILURE = 'NEWS_FETCH_FAILURE';
export const NEWS_FETCH_SUCCESS = 'NEWS_FETCH_SUCCESS';
export function preFetching() {
return {
type: NEWS_FETCH_LOADING,
}
}
export function successFetching(json) {
return {
type: NEWS_FETCH_SUCCESS,
payload: json
}
}
export function failureFetching(error) {
return {
type: NEWS_FETCH_FAILURE,
payload: error
}
}
My reducers:
...
import { NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS } from './actions'
const INITIAL_STATE = {
isFetching: false,
isSuccess: false,
items: []
};
function NewsListReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case NEWS_FETCH_LOADING:
case NEWS_FETCH_FAILURE:
return Object.assign({}, state, {
isFetching: true,
isSuccess: false
})
case NEWS_FETCH_SUCCESS:
return Object.assign({}, state, {
isFetching: false,
isSuccess: true,
items: action.payload,
})
default:
return Object.assign({}, state)
}
}
export const rootReducer = combineReducers({
NewsListReducer
})
export default rootReducer
My saga:
import { call, put, select, takeLatest } from 'redux-saga/effects';
import {NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS, preFetching, successFetching, failureFetching} from './actions';
import request from 'utils/request';
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put(successFetching(req));
} catch (err) {
yield put(failureFetching(err));
}
}
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
EDIT:
#Alleo Indong, i tried your advice and its almost work.
I change in component mapDispatchToProps to
export function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(loadNews()),
};
}
And add new function to actions.js
export function loadNews() {
return {
type: NEWS_FETCH_SUCCESS,
}
}
But now ajax sent every seconds like in while cycle. I tried call this.props.getData(); in componentDidMount and in constructorand result same.
EDIT 2
In component i add
import * as actionCreators from './actions';
import { bindActionCreators } from 'redux';
In constructor i change
constructor(props) {
super(props);
const {dispatch} = this.props;
this.boundActionCreators = bindActionCreators(actionCreators, dispatch)
}
But here dispatch is undefined and in componentDidMount too.
And change mapDispatchToProps to
export function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}

Hello #AND and welcome to stackoverflow! As I mentioned in the comment on the main post, you are dispatching a GENERATOR instead of an object on your
dispatch(saga());
Here's an example to help you
On your component import the actions that you want to use like this.
import * actionCreators from './actions';
import { bindActionCreators } from 'redux';
Learn more about bindActionCreators here enter link description here
This will import all of the exported actionCreators that you created there.
In my opinion you don't need successFetching and failureFetching anymore as you can dispatch this actions later on on your saga
Then in your mapDispatchToProps you would want to register this actioncreator
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
Then on your saga, I want to point up some problems here as well.
First your function
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
Is actually what we call a watcher where it watch when a certain action was dispatch, in this function you are already waiting for the NEWS_FETCH_SUCCESS before getNews is called which is wrong, because you first have do the FETCHING before you will know if it is failed or success so yeah this function should be like this
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_LOADING, getNews);
}
This simple means that you will take all the latestNEWS_FETCH_LOADINGactions that was dispatched and will call thegetNews`.
then on your getNews generator function, you can do it like this
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put({type: NEWS_FETCH_SUCCESS, req});
} catch (err) {
yield put({type: NEWS_FETCH_FAILED, err});
}
}
in here
const req = yield call(request, requestURL);
You are saying that you will wait for the result of the request / service that you called, it might be a promise.
Then in here, this is why you won't need the functions successFetching and failureFetching functions anymore, since you can do it like this
yield put({type: NEWS_FETCH_SUCCESS, req});
One last important step that you have to do now is to call the actionCreator inside your componentDidMount()
like this
componentDidMount() {
const { preFetching } = this.props;
preFetching();
}

Related

How do I reference a variable in redux store from a component index.js (react.js)

I'm a newbie in redux and react.js,
I am trying to make a button disappear on a component in react.js by putting an if condition on the state variable (articlesTable/index.js), which is connected to the redux library function on another file (actions/actionArticles.js), when a button on articlesTable/index.js is clicked, the component is connected with actions/actionArticles.js and dispatch a function in actions/actionArticles.js, which is called loadMoreData().
The function I am trying to configure the state in redux is,
in articlesActions.js
export const loadMoreArticles = () => async (dispatch, getState) => {
const lastArticleKey = Object.keys(getState().articlesMap).pop();
const lastArticle = getState().articlesMap[lastArticleKey];
console.log("articleMap", getState().articlesMap);
console.log("Last article", lastArticleKey, lastArticle);
let filteredArticles = {};
const uid = getState().auth.uid;
const userLevel = getState().profile.userLevel;
} else {
const filteredArticlesArray = [];
var lastArticleReached = false;
...
var lastArticleInArray = filteredArticlesArray[filteredArticlesArray.length-1];
if (lastArticleInArray[0]===lastArticleKey) {
console.log("Bingo, last article reached!");
lastArticleReached = true;
}
else if (lastArticleInArray[0]!== lastArticleKey)
{
console.log("Not last article");
lastArticleReached = false;
}
filteredArticles = Object.fromEntries(filteredArticlesArray.reverse());
}
dispatch({type: LAST_ARTICLE_REACHED, payload: lastArticleReached})
...
};
I dispatch this function with
dispatch({ type: LOAD_MORE_ARTICLES, payload: filteredArticles });
in the code snippet above
The root reducer looks like this,
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import articlesStatusReducer from './articlesStatusReducer';
const rootReducer = combineReducers({
...
articlesStatus: articlesStatusReducer,
form: formReducer,
...
});
export default rootReducer;
In articleStatusReducer,
import {LAST_ARTICLE_REACHED} from "../actions/types";
export default function(state = {}, action) {
switch(action.type) {
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return action.payload;
default:
return state;
}
}
In the articlesTable/index.js, I connect like this
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
}
};
const mapDispatchToProps = (dispatch) => {
return {
getArticlesWithData: () => dispatch(getArticlesWithData()),
loadMore: () => dispatch(loadMoreArticles())
}
};
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(ArticlesTable)
For some reason, articleStatus isn't recognised and when I do
console.log(this.props.articleStatus)
state.articleStatus is undefined
How can I reference state.articleStatus which should be boolean ?
Edit:
For some reason when I put it in a conditional JSX brackets in the render method, it prints out undefined
render () => {
{
console.log(this.props.lastArticleReached),
!this.props.lastArticleReached
: <Button> </Button>
?
<div><div>
}
}``
In function mapStateToProps, you should map state.articleStatus to a props.
somethings like this:
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
articleStatus: state.articleStatus
}
};
So this.props.articleStatus will works . :)
The problem is in your reducer. Each case of your reducer must return the state but in your case, your return action.payload.
try something like this.
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return {...state, articleStatus: action.payload};
like this, articlesStatus became an object with one props, articleStatus, your boolean.
I tried another name for the props but with similar method as Thomas Caillard,
Reducer.js
case(REACH_LAST_ARTICLE):
return {...state, lastArticleReached: action.payload}
in component index.js
const mapStateToProps = (state) => {
return {
...
lastArticleReached: state.articlesMap.lastArticleReached
...
}
};
Thanks for all the helps so far

How can I refactor this ASYNC call in my react component to make it more readable?

I want my component to fetch an array of objects from the server. Each object is a message with author, body and date. I then want to render these messages in my react component.
My react component currently fetches data from the server before mounting. It will then store this message list in the redux state.|
I'm sure there's a better way of writing this code.
1. Can I place the fetch request in either the Action or Reducer file?
2. Can I write a function in the component to make the async call?
import React, { Component } from 'react';
import Message from '../components/message.jsx';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
// Actions
import { fetchMessages } from '../actions/actions_index.js';
class MessageList extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
fetch('https://wagon-chat.herokuapp.com/general/messages')
.then(response => response.json(),
error => console.log('An error occured receiving messages', error))
.then((data) => {
this.props.fetchMessages(data.messages);
});
}
render() {
return (
<div className="message-list">
{this.props.messageList.map( (message, index) => { return <Message key={index} message={message}/> })}
</div>
)
}
}
function mapStateToProps(state) {
return {
messageList: state.messageList
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ fetchMessages: fetchMessages },
dispatch
)
}
export default connect(mapStateToProps, mapDispatchToProps)(MessageList);
Can I place the fetch request in either the Action or Reducer file?
The fetch request should be placed in action creator. Where the retrieved data will be dispatched to reducer later to manipulate the data, and lastly update the store to show on UI. Here's simple flow for most of react-redux app.
UI -> Action creator (calling request, saga etc..) -> reducer -> store -> UI
Can I write a function in the component to make the async call?
Yes, this should be called action creator, and you can see actions.js below for more reference.
I think you can safely follow this sample pattern where most tutorials out there apply. I'm assuming all files listed here are in the same directory.
constant.js
const MESSAGE_FETCH__SUCCESS = 'MESSAGE/FETCH__SUCCESS'
const MESSAGE_FETCH__ERROR = 'MESSAGE/FETCH__ERROR'
export {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
}
actions.js
import {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
} from './constant';
const fetchMessageError = () => ({
type: MESSAGE_FETCH__ERROR
})
const fetchMessageSuccess = data => ({
type: MESSAGE_FETCH__SUCCESS,
payload: data
})
const fetchMessages = () => {
const data = fetch(...);
// if error
if (data.error)
fetchMessageError();
else fetchMessageSuccess(data.data);
}
export {
fetchMessages
}
reducers.js
import {
MESSAGE_FETCH__SUCCESS,
MESSAGE_FETCH__ERROR
} from './constant';
const INIT_STATE = {
messageList: []
}
export default function( state = INIT_STATE, action ) {
switch(action.type) {
case MESSAGE_FETCH__SUCCESS:
return {
...state,
messageList: action.payload
}
case MESSAGE_FETCH__ERROR:
// Do whatever you want here for an error case
return {
...state
}
default:
return state;
}
}
index.js
Please read the comment I noted
import React, { Component } from 'react';
import Message from '../components/message.jsx';
import { connect } from 'react-redux';
// Actions
import { fetchMessages } from './actions';
class MessageList extends Component {
/* If you don't do anything in the constructor, it's okay to remove calling `constructor(props)`
*/
//constructor(props) {
// super(props)
//}
// I usually put this async call in `componentDidMount` method
componentWillMount() {
this.props.fetchMessage();
}
render() {
return (
<div className="message-list">
{
/* Each message should have an unique id so they can be used
for `key` index. Do not use `index` as an value to `key`.
See this useful link for more reference: https://stackoverflow.com/questions/28329382/understanding-unique-keys-for-array-children-in-react-js
*/
this.props.messageList.map( message => <Message key={message.id} message={message}/> )
}
</div>
)
}
}
function mapStateToProps(state) {
return {
messageList: state.messageList
}
}
export default connect(mapStateToProps, {
fetchMessages
})(MessageList);
You could use redux-thunk in an action called getMessages.
So:
(The double arrow func, is to return an action, see redux-thunk)
const getMessages = ()=>(dispatch, getState)=>{
fetch('https://wagon-chat.herokuapp.com/general/messages')
.then(response => response.json(),
error => dispatch(['error', error]))
.then((data) => {
dispatch(data);
})
}
Then you've successfully reduced your component to:
componentWillMount(){
this.props.getMessages()
}
I think #Duc_Hong answered the question.
And in my opinion, I suggest using the side-effect middle-ware to make AJAX call more structured, so that we could handle more complicated scenarios (e.g. cancel the ajax request, multiple request in the same time) and make it more testable.
Here's the code snippet using Redux Saga
// Actions.js
const FOO_FETCH_START = 'FOO\FETCH_START'
function action(type, payload={}) {
return {type, payload};
}
export const startFetch = () => action{FOO_FETCH_START, payload);
// reducer.js
export const foo = (state = {status: 'loading'}, action) => {
switch (action.type) {
case FOO_FETCH_STARTED: {
return _.assign({}, state, {status: 'start fetching', foo: null});
}
case FOO_FETCH_SUCCESS: {
return _.assign({}, state, {status: 'success', foo: action.data});
}
......
}
};
Can I place the fetch request in either the Action or Reducer file?
// Saga.js, I put the ajax call (fetch, axios whatever you want) here.
export function* fetchFoo() {
const response = yield call(fetch, url);
yield put({type: FOO_FETCH_SUCCESS, reponse.data});
}
// This function will be used in `rootSaga()`, it's a listener for the action FOO_FETCH_START
export function* fooSagas() {
yield takeEvery(FOO_FETCH_START, fetchFoo);
}
Can I write a function in the component to make the async call?
// React component, I trigger the fetch by an action creation in componentDidMount
class Foo extends React.Component {
componentDidMount() {
this.props.startFetch();
}
render() {
<div>
{this.props.foo.data ? this.props.foo.data : 'Loading....'}
<div>
}
}
const mapStateToProps = (state) => ({foo: state.foo});
const mapDispatchToProps = { startFetch }
export default connect(mapStateToProps, mapDispatchToProps) (Foo);
//client.js, link up saga, redux, and React Component
const render = App => {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combinedReducers,
initialState,
composeEnhancers(applyMiddleware(sagaMiddleware))
);
store.runSaga(rootSaga);
return ReactDOM.hydrate(
<ReduxProvider store={store}>
<BrowserRouter><AppContainer><App/></AppContainer></BrowserRouter>
</ReduxProvider>,
document.getElementById('root')
);
}

Redux Saga not triggered/api not called - props undefined

I'm learning redux-saga and having a problem with calling my api.
It seems like my saga is not triggered and my props stay undefined.
I'm using fake-server for mocking data.
I tested the server and it seems to be working fine outside the saga.
My code looks like this:
UserView.js
const UserView = (props) => {
const {user} = props;
return (
<Header as='h2'>{user.name}</Header>
);
}
export default UserView
actions.js
export function showUserRequest(){
return{
type: USER_REQUEST
}
}
export function showUserSuccess(user) {
return {
type: USER_SUCCESS,
payload: user
}
}
export function showUserError(error){
return{
type: USER_ERROR,
payload: error
}
}
reducer.js
export default function (state = initialState, action) {
switch(action.type){
case USER_REQUEST:
return{
...state,
requesting: true,
successful: false,
errors: []
}
case USER_SUCCESS:
return{
...state,
user: action.payload,
requesting: false,
successful: true,
errors: [],
}
case USER_ERROR:
return{
requesting: false,
successful: false,
errors: state.errors.concat[{
body: action.error.toString(),
time: new Date(),
}],
}
default:
return state;
}
}
user.js
class User extends Component {
constructor(props) {
super(props);
this.fetchUser = this.fetchUser.bind(this);
}
componentWillMount(){
this.fetchUser();
}
fetchUser(){
const { showUserRequest } = this.props;
return showUserRequest;
}
render() {
const user = this.props;
return (<UserView user = {user.user}/>);
}
}
function mapStateToProps(state) {
return {
user: state.user
};
}
export default connect(mapStateToProps, {showUserRequest})(User);
sagas.js
const api = 'http://localhost:8080/user';
function userRequestApi () {
return axios.get(api)
}
function* userRequestFlow() { //does not seem to get invoked
try {
const user = yield call(userRequestApi);
yield put(showUserSuccess(user))
} catch (error) {
yield put(showUserError(error))
}
}
function* userWatcher() { //seems to get invoked
yield takeLatest(USER_REQUEST, userRequestFlow);
}
export default userWatcher
indexSaga.js/indexReducer.js
export default function* IndexSaga() {
yield all([
SignupSaga(),
LoginSaga(),
UserSaga()
]);
}
const IndexReducer = combineReducers({
route: routerReducer,
form: formReducer,
user: userReducer
})
export default IndexReducer
It seems like the userRequestFlow function is not called at all.
I'm sure it's just a silly rookie mistake but I just can't figure it out.
What am I doing wrong?
In your user.js you need to define mapDispatchToProps() and dispatch the showUserRequest action like this:
function mapDispatchToProps(dispatch) {
return {
dispatchUserRequest: () => dispatch(showUserRequest()),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(User);
And then invoke this.props.dispatchUserRequest() when you wish to dispatch the action.

Reactjs+Redux: Retrieve this.props value after onClick async call

I'm working my way learning some react+redux-thunk and I've put together a simple form that hits an API and retrieves some jokes. My core component and code:
containers/AsyncApp.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {bindActionCreators} from 'redux';
import SearchJokes from '../components/SearchJokes';
import Jokes from '../components/Jokes';
import {fetchJokes} from '../actions';
class AsyncApp extends Component {
constructor(props) {
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleInput = this.handleInput.bind(this)
this.state = {searchText: ''};
}
handleSubmit(e){
e.preventDefault();
//const {searchText} = this.props;
console.log('button clicked ' + this.state.searchText);
this.props.fetchJokes(this.state.searchText);
}
handleInput = (e) => {
this.setState({
searchText: e.target.value,
})
}
render(){
const { jokes, isFetching } = this.props
return(
<div>
<SearchJokes
handleSubmit={this.handleSubmit}
onChange={this.handleInput}
searchText={this.state.searchText}
/>
{jokes ? (<Jokes jokes={jokes}/>) : (<div></div>)}
</div>
)
}
}
function mapStateToProps(state) {
return {
isFetching: state.isFetching,
jokes: state.items
}
}
function mapDispatchToProps(dispatch) {
return{
fetchJokes: bindActionCreators(fetchJokes, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AsyncApp)
actions/index.js
export const REQUEST_JOKES = 'REQUEST_JOKES'
export const RECEIVE_JOKES = 'RECEIVE_JOKES'
function requestJokes(term) {
return {
type: REQUEST_JOKES,
term
}
}
function receiveJokes(term, json) {
return {
type: RECEIVE_JOKES,
term,
jokes: json.results.map(joke => joke)
}
}
export function fetchJokes(term) {
return dispatch => {
dispatch(requestJokes(term))
return fetch(`https://icanhazdadjoke.com/search?term=${term}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
}).then(response => response.json())
.then(json => dispatch(receiveJokes(term, json)))
}
}
reducers/index.js
import { combineReducers } from 'redux'
import {
REQUEST_JOKES,
RECEIVE_JOKES
} from '../actions'
function jokesBySearch(state = {}, action) {
switch(action.type){
case REQUEST_JOKES:
return Object.assign({}, state, {isFetching: true, items: []})
case RECEIVE_JOKES:
return Object.assign({}, state, {
isFetching: false,
items: action.jokes,
})
default:
return state
}
}
const rootReducer = combineReducers({
jokesBySearch,
})
export default rootReducer
The form component works, and I can see the json array returned as part of the action. But the value of this.props.jokes is empty or undefined in the console.log and I'm wondering how to populate it once the results from the API call are returned.
I see two problems in this code.
First you don't need to use combineReducers when you only have one reducer. Just keep it simple and add complexity only when absolutely necessary. Instead of combineReducers
export default jokesBySearch
If you do use combineReducers be mindful how it affects your state.
Second there is no need to use bindactioncreators here, just dispatch the action (and don't forget to pass the term).
function mapDispatchToProps(dispatch) {
return{
fetchJokes: (term) => dispatch(fetchJokes(term))
}
}
See here when you need to use bindactioncreators (pretty rare use case)
In general good way to lear a library like redux is just use the core functionality until you encounter problem and only then add complexity.

React/Redux: Component not redrawing despite correct dispatching and state change. (no mutation involved)

I have a problem with my redux app: it correctly dispatches the relevant action and updates the state, but for some reason the UI doesn't get updated. 99% of questions about this seem to be caused because the state actually gets mutated, but I'm pretty sure that's not the case. Here are the relevant files:
Container component:
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import requestEvents from 'actions/feedActions';
import { FeedElement, feedElementTypes } from '../components/feed/feedElement';
class Feed extends React.Component {
componentWillMount() {
this.props.requestEvents();
}
render() {
console.log('Rendering feed');
const listOfFeedElements = this.props.listOfFeedElements;
let elementsToDisplay;
if (listOfFeedElements.length === 0) {
elementsToDisplay = <li><p> No elements to display</p></li>;
} else {
const numOfElementsToDisplay = listOfFeedElements.length <= 10 ?
listOfFeedElements.length : 10;
const sortedElementsToDisplay = listOfFeedElements.concat().sort(
(e1, e2) => e1.timestamp - e2.timeStamp);
elementsToDisplay =
sortedElementsToDisplay
.slice(0, numOfElementsToDisplay)
.map(el => FeedElement(el));
}
return (
<div className="socialFeedContainer">
<h1> Social feed </h1>
<ol>
{elementsToDisplay}
</ol>
</div>
);
}
}
Feed.propTypes = {
requestEvents: PropTypes.func.isRequired,
listOfFeedElements: PropTypes.arrayOf(PropTypes.shape({
timeStamp: PropTypes.number.isRequired,
type: PropTypes.oneOf(Object.keys(feedElementTypes)).isRequired,
targetDeck: PropTypes.string.isRequired,
concernedUser: PropTypes.string.isRequired,
viewed: PropTypes.bool.isRequired })),
};
Feed.defaultProps = {
listOfFeedElements: [],
};
function mapDispatchToProps(dispatch) {
return bindActionCreators({ requestEvents }, dispatch);
}
function mapStateToProps(state) {
const { feedState } = state.local.feed.listOfFeedElements;
return {
feedState,
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Feed);
Reducers:
import Immutable from 'seamless-immutable';
import { types } from 'actions/feedActions';
const initialState = Immutable({
listOfFeedElements: [],
sentRequestForList: false,
receivedList: false,
});
function feedReducer(state = initialState, action) {
switch (action.type) {
case types.REQUEST_FEED_ELEMENTS:
return Immutable.merge(state, {
sentRequestForList: true,
receivedList: false,
});
case types.RECEIVED_LIST:
return Immutable.merge(state, {
sentRequestForList: false,
receivedList: true,
listOfFeedElements: action.payload.listOfNotifications,
});
default:
return state;
}
}
export default feedReducer;
Saga:
import { takeLatest, put } from 'redux-saga/effects';
import { types } from 'actions/feedActions';
import feedApiCall from './feedApiCall';
export function* getFeedFlow() {
try {
console.log('sent request for feed');
const listOfNotifications = feedApiCall();
yield put({
type: types.RECEIVED_LIST,
payload: {
listOfNotifications,
},
});
} catch (error) {
yield put({
type: types.RECEPTION_ERROR,
payload: {
message: error.message,
statusCode: error.statusCode,
},
});
}
}
function* feedUpdateWatcher() {
yield takeLatest(types.REQUEST_FEED_ELEMENTS, getFeedFlow);
}
export default feedUpdateWatcher;
The actions get dispatched and the state modified (In the end I have a list with the components). However the component renders only once, as I can check from the call to console.log.
The root component looks quite suspiciously.
First, proceeding from a code and agreements on naming, FeedElement the class looks as React, however it is used as simple function in map. Whether there was no similar look in view of something:
elementsToDisplay =
sortedElementsToDisplay
.slice(0, numOfElementsToDisplay)
.map(el => (<FeedElement {...el} />));
Secondly, you return object const {feedState} = state.local.feed.listOfFeedElements; and further you address this.props.listOfFeedElements - such field in principle just doesn't exist. And from where a part of state.local.feed undertakes?
In addition, time you is used by redux and saga, it is better to make generally a pure functional part and to get rid of a call of componentWillMount which not really corresponds to the expected architecture.

Categories