MapDispatchToProps returns a function - reactjs

I try to read my articles from my firebase DB but I can't make it work.
Here is my DB:
myDB-myid
articles
1
body: "Lorem Ipsum Dolor si damet blablabla"
createdAt: 12345
subtitle: "ça promet beaucoup d'autres supers articles"
title: "Mon premier article"
2
body: "Encore du Lorem Ipsum et bla et bla et bla..."
createdAt: 34567
subtitle: "Vous avez aimé le premier ? Le deuxième va vous..."
title: "Et voici le deuxième article"
my component that should render the list of all articles, ArticlePage:
import React from 'react';
import { connect } from 'react-redux';
import Article from './Article';
import { startSetArticles } from '../../actions/articles';
const ArticlesPage = props => (
<div className="articles-wrapper">
<div className="container">
{console.log(props.articles)}
{
props.articles.length === 0 ? (
<p>No article</p>
) : (
props.articles.map(a => {
return <Article key={a.id} {...a}/>
})
)
}
</div>
</div>
);
const mapDispatchToProps = dispatch => ({
articles: () => dispatch(startSetArticles())
});
export default connect(undefined, mapDispatchToProps)(ArticlesPage);
my store:
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import authReducer from '../reducers/auth';
import articlesReducer from '../reducers/articles';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(
combineReducers({
auth: authReducer,
articles: articlesReducer
}),
composeEnhancers(applyMiddleware(thunk))
);
return store;
};
my actions in action/articles.js:
import database from '../firebase/firebase';
// SET_ARTICLES
export const setArticles = articles => ({
type: 'SET_ARTICLES',
articles
});
export const startSetArticles = () => {
return dispatch => {
return database.ref('articles').once('value').then(snapshot => {
const articles = [];
snapshot.forEach(a => {
articles.push({
id: a.key,
...a.val()
});
});
dispatch(setArticles(articles));
});
};
};
my reducer in reducers/articles.js:
const articlesReducerDefaultState = [];
export default (state = articlesReducerDefaultState, action) => {
switch (action.type) {
case 'SET_ARTICLES':
return action.articles;
default:
return state;
}
};
But I can't make it work. Here is the console output of the console.log(props.articles) from the component:
ƒ articles() {
return dispatch((0, _articles.startSetArticles)());
}
Any Idea ? It should render the two posts but it only returns the function itself. (I get a 'no article' from the ternary operator)

articles is a function because that's exactly what you do:
const mapDispatchToProps = dispatch => ({
articles: () => dispatch(startSetArticles())
});
you map dispatch to props and you mapped it to a prop called articles which is a function that dispatches an action. No surprise here. But I guess what you also want is to map state to props which you can do with a first argument of connect:
const mapStateToProps = state => ({ articlesList: state.articles });
export default connect(mapStateToProps, mapDispatchToProps)(ArticlesPage);
Now if you do console.log(props) you should get Object with two properties:
{
articles, // a function from mapDispatchToProps
articlesList // a function from mapStateToProps
}
EDIT:
You still however need to call your articles function to fetch data with firebase and populate the redux store. Best way to start asynchronous data is to use componentDidMount() hook:
class ArticlesPage extends React.Component {
componentDidMount() {
this.props.articles(); // use mapped function to dispatch redux action
}
render() {
// render data with array from the store
return (
<div className="articles-wrapper">
<div className="container">
{this.props.articlesList.length === 0 ? (
<p>No article</p>
) : (
this.props.articlesList.map(a => {
return <Article key={a.id} {...a} />;
})
)}
</div>
</div>
);
}
}

I think you only connected to the store, but never called the function startSetArticles(). Try add(this will work with your current mapDispatchToProps)
componentDidMount() {
this.props.articles()
}
This should call the function, which should update your store.
And I would recommend instead of:
const mapDispatchToProps = dispatch => ({
articles: () => dispatch(startSetArticles())
});
You should just do this:
const mapDispatchToProps = dispatch => ({
startSetArticles: () => dispatch(startSetArticles())
});
Then you would have to do this, which makes more sense.
componentDidMount() {
this.props.startSetArticles()
}
I think your confusion is about how to use mapDispatchToProps. When you mapDispatchToProps, it DOES NOT call that function right away. You will still need to call that function somewhere in the component. In your case componenetDidMount makes sense.
And then you need to add mapStateToProps, which will connect to the store and actually get the data back.
Here is a working example:
export class Morphology extends React.Component {
componentDidMount() {
document.title = 'Eflows | Morphology';
this.props.fetchGeoSites();
}
render() {
return (
<div>
<div style={styles.banner} />
<Layout geoSites={this.props.geoSites} />
</div>
);
}
}
Morphology.propTypes = {
fetchGeoSites: PropTypes.func,
geoSites: PropTypes.array,
};
const mapStateToProps = state => {
return {
geoSites: state.geoSite.geoSites,
};
};
const mapDispatchToProps = dispatch => {
return {
fetchGeoSites: () => dispatch(fetchGeoSites()),
};
};
I am using the same method, you can look into it more with this repo:
https://github.com/leogoesger/eflow-client

You should use the first parameter of connect to get the articles state.
connect(state => ({ articles: state.articles }), ...)
To set the articles state, you need to call startSetArticles somewhere like componentDidMount or in the file where you declare your store (if you want it to be called when the app loads) with store.dispatch(startSetArticles()).

Related

How to rerender when Redux state is changed

Hi I have 2 components.
The first component provides a read (useSelector) from the Redux state object and renders its contents
The second component ensures the insertion of new data into the same Redux state object
How to achieve that when a Redux state object changes with the help of the second component, the first component captures this change and renders the new content of the object again.
I tried to add in the component element:
useEffect(() => {
...some actions
}, [reduxStateObject]);
But it gives me too many requests.
/// EDIT add real example
component
import React from "react";
import { useSelector } from "react-redux";
const ToDoList = () => {
const { todos } = useSelector((state) => state.global);
return (
<div>
<h1>Active</h1>
{todos
?.filter((todo) => !todo.isCompleted)
.sort((a, b) => (a.deadline < b.deadline ? 1 : -1))
.map((todo, id) => {
const date = new Date(todo.deadline).toLocaleString();
return (
<div key={id}>
<p>{todo.text}</p>
<p>{date}</p>
</div>
);
})}
</div>
);
};
export default ToDoList;
component
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { getToDoItems } from "../redux/globalSlice";
import ToDoList from "../components/ToDoList";
const ToDoWall = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getToDoItems(1));
}, [dispatch]);
const submitForm = (e) => {
dispatch(postToDoItem(e.data));
};
return (
<>
<ToDoList />
<form onSubmit={submitForm}>
<input type="text"></input>
<input type="submit" value="" />
</form>
</>
);
};
export default ToDoWall;
/// EDIT add Reducer
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: null,
};
export const globalSlice = createSlice({
name: "global",
initialState,
reducers: {
setItems: (state, action) => {
state.todos = action.payload;
},
},
});
export const { setItems } = globalSlice.actions;
export default globalSlice.reducer;
// Load todo items
export const getToDoItems = (id) => {
return (dispatch) => {
axios
.get(`https://xxx.mockapi.io/api/list/${id}/todos`)
.then((resp) => dispatch(setItems(resp.data)));
};
};
// Post a list name
export const postNameList = (data) => {
return (dispatch) => {
axios.post("https://xxx.mockapi.io/api/list", {
name: data,
});
};
};
// Post a todo item
export const postToDoItem = (id, data) => {
return (dispatch) => {
axios.post(
`https://xxx.mockapi.io/api/list/${id}/todos`,
{
listId: id,
title: data.title,
text: data.text,
deadline: +new Date(data.deadline),
isCompleted: false,
}
);
};
};
As far as I understood, you don't need to do anything. When you dispatch action to change state in redux store, it'll change, and all components that use that state will get it, you don't need to worry about updating anything.

react redux with axios api calls

I am trying to learn the react and for that I am trying to create a sample todo app. I have a python flask backend which servers as REST server and react as web server.
Everything works find an I am able to show todos and delete particular todo as well. However now I have started learning Redux, and that seems really confusing.
I am not sure how to make call to my rest server. Following just returns promise, not sure how to get the data, rather than promise.
store.js
import axios from 'axios'
import { createStore } from 'redux'
export const ADD_TODO = 'ADD_TODO'
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const listTodo = todos => ({
type: 'LIST_TODO',
todos
})
const add_todo = (id, text) => {
return axios.post("http://localhost:5001/todos", {id:id, data:text})
.then(Response=>{
store.dispatch(addTodo(Response.data));
})
}
const fetch_data = () => {
return axios.get("http://localhost:5001/todos")
.then(Response=>{
store.dispatch(listTodo(Response.data))
})
}
const initialState ={
todos: {},
new_todo: ''
}
function todoApp(state = initialState, action) {
console.log("reducer called...")
switch (action.type) {
case ADD_TODO:
return Object.assign({}, state, {
new_todo: action.text
})
default:
return state
}
}
const store = createStore(todoApp)
export default store
app.js
import React, {Component} from 'react'
import {connect} from 'react-redux'
class App extends Component{
render(){
return(
<div>
<button onClick={this.props.addTodo('testing')}>fetch_Data</button>
</div>
);
}
}
export default connect() (App)
index.js
ReactDOM.render(<Provider store={store}> <App /> </Provider>,
document.getElementById('root'));
Firstly, you should export the actions you have created which will then be imported and used in the components using the connect HOC.
You can dispatch the 'fetch_data' action to get the data in your component. Also, you can dispatch 'addTodo' action to add new todo in the list.
export const ADD_TODO = 'ADD_TODO';
export const GET_TODO = 'GET_TODO';
export const fetch_data = () => {
return (dispatch) => axios.get("http://localhost:5001/todos")
.then(response => {
dispatch({type: GET_TODO, todos: response.data});
})
}
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text: text
});
Use the actions constants like ADD_TODO, GET_TODO to save or to update the redux state in reducers
const todoApp = (state = initialState, action) => {
console.log("reducer called...")
switch (action.type) {
case ADD_TODO:
const todos = {...state.todos};
todos[action.id] = action.text;
return Object.assign({}, state, {
todos: todos
});
case GET_TODO:
return Object.assign({}, state, {
todos: action.todos
});
default:
return state
}
}
Importing the actions and then call the function you have added in the 'mapDispatchToProps' to dispatch the actions.
import React, {Component} from 'react'
import {connect} from 'react-redux';
import { addTodo, fetch_data } from "../store";
class App extends Component{
render(){
return(
<div>
<button onClick={this.props.addTodo(todoId, 'testing')}>fetch_Data</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
todos: state.todoApp.todos
});
const mapDispatchToProps = (dispatch) => ({
addTodo: (id, text) => dispatch(addTodo(id, text)),
fetch_data: () => dispatch(fetch_data())
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
redux is based on actions and reducers, basically reducers are pure functions which means no side effects as for example api calls, I'd advice you read more about redux and how to use redux with redux-chunk for making api calls
You make this work like this. You need to dispatch action when you have response.
const fetch_data = () => {
return axios.get("http://localhost:5001/todos")
.then(Response=>{
store.dispatch(addTodo(Response.data));
})
}
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text: text
})

My state changes when the component is re-rendered

I have a web app that fetches recipe from a backend API. When the feed component mounts, I set an axios get method to receive data from the API and update my redux store and then update the components state to the props matched to state of the redux store using mapPropsToState.
It works when the component is rendered initially, but moving to another component, say Create Recipe and then switching back to the Feed component, the content flashes for a mini second ad then disappears. And shows 'No Recipes To Show' which is what I set to display when there are no recipes.
I have tried using the setState in the componentDidMount method and then also in the .then method of axios, and also in both, simultaneously. Still same result. I have also tried logging the state to the console and it shows that it received the data well all the times that I switched back and forth between components, but the data wont display on screen.
FEED.JS
import React, {Component} from 'react';
import RecipeCard from './RecipeCard';
import {connect} from 'react-redux';
import {updateRecipes} from '../actions/recipeActions'
import axios from 'axios'
class Feed extends Component {
state = {
recipes: []
};
feedTitleStyle = {
color: 'rgba(230, 126, 34, 1)',
margin: '28px 0'
};
componentDidMount() {
axios.get('http://127.0.0.1:8000/api/recipes/')
.then(res =>{
console.log(res);
this.props.updateRecipesFromAPI(res.data);
this.setState({
recipes: this.props.recipes
})
})
.catch(err => {
console.log(err)
});
let recipes = [...this.state.recipes, this.props.recipes];
this.setState({
recipes
})
}
render() {
const {recipes} = this.state;
console.log(this.props.recipes);
console.log(recipes);
const recipesList = recipes.length ? (
recipes.map(recipe => {
return (
<div className="container" key={recipe.id}>
<div className='col-md-10 md-offset-1 col-lg-9 mx-auto'>
<div className="row">
<div className="col s12 m7">
<RecipeCard recipe={recipe}/>
</div>
</div>
</div>
</div>
)
})
) : (
<div className='center'>No recipes yet</div>
);
return (
<div>
<div className='container'>
<h4 style={this.feedTitleStyle} className='center feed-title'>Feed</h4>
{recipesList}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return{
recipes: state.recipes
}
};
const mapDispatchToProps = (dispatch) => {
return {
updateRecipesFromAPI: (recipes) => {dispatch({
type: 'UPDATE_RECIPES',
recipes
}}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Feed)
Here is my reducer:
const initialState = {
recipes: [],
};
const recipeReducer = (state = initialState, action) => {
switch (action.type) {
case 'UPDATE_RECIPES':
let updatedRecipes = [...state.recipes, action.recipes];
console.log(updatedRecipes[0]);
return {
...state,
recipes: updatedRecipes[0]
};
default:
return state
}
};
export default recipeReducer
You are juggling between REDUX and State which is wrong, you should not be doing this, instead, the ideal solution would be to stick on with REDUX and let REDUX do the async call and fill in the store, and make use of the mapStateToProps to get it into props.
use Action Creators ( Async ) to solve this, you should be using middleware like thunk (Thunk) to do this.
Action creators:
export const updateRecipesFromAPI_Async = () => { // async action creator
return dispatch => {
axios.post('http://127.0.0.1:8000/api/recipes/')
.then(response => {
console.log(response.data);
dispatch(updateRecipesFromAPI_Success(response.data.name, orderData)); // calls a sync action creator
})
.catch(error => {
console.log(error);
});
}
}
export const updateRecipesFromAPI_Success = (recipes) => { // sync action creator
return {
type: 'UPDATE_RECIPES',
orderData: recipes
}
}

TypeError: this.setState is not a function, inside Redux Actions file

I'm attempting to reconfigure a PixaBay clone application to Redux. The application retrieves photos as the user types a search text. However, it breaks as soon as I type inside the input.
From what I've researched, you can only call setState in a class so I gave fetchPhotos an arrow function, but that didn't work. I also tried to .bind(this), but that gave me a parsing error. Could someone kindly tell me what I'm doing wrong? Here are the following errors, along with my code.
ERRORS
TypeError: this.setState is not a function
fetchPhotos
src/actions/actions.js:10
7 |
8 | export function fetchPhotos(e) {
9 | const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
> 10 | const request = this.setState({searchText: e.target.value}, () => {
11 | axios.get(url)
12 | .then(response => {
13 | this.setState({images: response.data.hits});
fetchPhotos
node_modules/redux/es/redux.js:475
Search._this.FetchPhotosHandler [as onChange]
src/components/search/Search.js:11
8 | class Search extends Component {
9 |
10 | FetchPhotosHandler = (e) => {
> 11 | this.props.fetchPhotos(e);
12 | }
13 |
14 | render() {
SEARCH CONTAINER
import React, { Component } from 'react';
import { fetchPhotos } from '../../actions/actions';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import TextField from 'material-ui/TextField';
import ImageResults from '../imageResults/ImageResults';
class Search extends Component {
state = {
searchText: '',
images: []
}
FetchPhotosHandler = (e) => {
this.props.fetchPhotos(e);
}
render() {
return (
<div>
<TextField
name="searchText"
value={this.props.searchText}
onChange={this.FetchPhotosHandler}
floatingLabelText="Search for photos"
fullWidth={true} />
<br />
<ImageResults images={this.props.images} />
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchPhotos, dispatch});
}
export default connect(null, mapDispatchToProps)(Search);
ACTION
import axios from 'axios';
export const FETCH_PHOTOS = 'FETCH_PHOTOS';
const ROOT_URL = 'https://pixabay.com/api';
const API_KEY = '10264275-868d83de96a4d0c47db26f9e0';
const searchText = '';
export function fetchPhotos(e) {
const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
const request = this.setState({searchText: e.target.value}, () => {
axios.get(url)
.then(response => {
this.setState({images: response.data.hits});
})
.catch(error => {
console.log(error)
});
});
return {
type: FETCH_PHOTOS,
payload: request
};
}
REDUCER
import { FETCH_PHOTOS } from '../actions/actions';
const initialState = {
searchText: '',
images: []
}
const reducer = (state = initialState, action) => {
switch(action.type) {
case FETCH_PHOTOS:
return {
...state,
images: action.data.hits
};
default:
return state;
}
}
export default reducer;
You should avoid attempting to use setState() in your action as it goes against Redux entirely. setState() is meant for managing the local of a React.Component. As you are attempting to utilize Redux, you should instead dispatch actions from your actions creators that update the store via your reducers and finally mapping store values to your component's props via connect(). Below is an example of your code restructured similar to the Async Redux example.
Instead of attempting to call setState() in the action, instead an action is dispatched containing the image payload. The Search component utilizes mapStateToProps (1st argument of connect()) to map store properties such the images array to the component's props. These props are used to render a list of data. This completely eliminates the need to have an images local state property on Search as values are being retrieved from the store as changes happen via actions/reducers. This example uses redux-thunk middleware to handle async actions, but there are plenty of other options out there that you could consider.
store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const middleware = [ thunk ];
const store = createStore(
rootReducer,
applyMiddleware(...middleware)
);
export default store;
actions:
export const FETCH_PHOTOS = 'FETCH_PHOTOS';
export const RECEIVE_PHOTOS = 'RECEIVE_PHOTOS';
// fake data
const testPhotos = [
{ id: 1, src: 'https://placehold.it/250' },
{ id: 2, src: 'https://placehold.it/250' }
];
// fake API call as promise
const getTestPhotos = () => {
return new Promise((resolve) => {
setTimeout(() => {
return resolve(testPhotos);
}, 500);
});
}
const fetchPhotos = (searchText) => ({
type: FETCH_PHOTOS
});
const receivePhotos = (photos) => ({
type: RECEIVE_PHOTOS,
data: {
hits: photos
}
});
export const searchPhotos = (searchText) => dispatch => {
// dispatch an action to handle loading/waiting for API response
dispatch(fetchPhotos(searchText));
// dispatch another action with payload within then()
return getTestPhotos()
.then(photos => dispatch(receivePhotos(photos)));
}
reducer:
import { FETCH_PHOTOS, RECEIVE_PHOTOS } from '../actions';
const initialState = {
loading: false,
searchText: '',
images: []
}
const photos = (state = initialState, action) => {
switch(action.type) {
case FETCH_PHOTOS:
return {
...state,
loading: true
};
case RECEIVE_PHOTOS:
return {
...state,
loading: false,
images: action.data.hits
};
default:
return state;
}
}
export default photos;
Search:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchPhotos } from './actions';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
searchText: ''
};
this.fetchPhotosHandler = this.fetchPhotosHandler.bind(this);
}
fetchPhotosHandler(e) {
const { value: searchText } = e.target;
this.setState({ ...this.state, searchText }, () => {
this.props.dispatch(searchPhotos(e));
})
}
render() {
const { loading, images } = this.props;
return (
<div>
<h1>Search</h1>
<div>
<label htmlFor="search">Search:</label>
<input name="search" id="search" type="text" value={this.state.searchText} onChange={this.fetchPhotosHandler} />
</div>
{loading ? (
<div>Loading...</div>
) : (
<ul>
{images.map(image => <li key={image.id}>{image.src}</li>)}
</ul>
)}
</div>
);
}
}
const mapStateToProps = ({ photos: { loading, images } }) => ({ loading, images });
export default connect(mapStateToProps)(Search);
I've created an example to show this functionality in action at a basic level.
Hopefully that helps!
You can bind component class instance to your action and it should work.
FetchPhotosHandler = (e) => {
this.props.fetchPhotos.bind(this)(e);
}
Since you have fetchPhotos exported from different module and in order to do setState there you need to pass this context to fetchPhotos as a param and use the param to do setState. That's how this context will be available
Pass this to fetchPhotos as a param
FetchPhotosHandler = (e) => {
this.props.fetchPhotos(e, this);
}
And here access this and do seState
export function fetchPhotos(e, this) {
const url = `${ROOT_URL}/?key=${API_KEY}&q=${searchText}&image_type=photo`;
const request = this.setState({searchText: e.target.value}, () => {
axios.get(url)
.then(response => {
this.setState({images: response.data.hits});
})
.catch(error => {
console.log(error)
});
});
return {
type: FETCH_PHOTOS,
payload: request
};
}

React Redux Store updating, but component not re-rendering

Using the terminal to test my dispatched actions, Redux-logger shows that my state is being correctly updated. However, my component is not re-rendering as a result of the state change. I've looked at the SO answers regarding component not re-rendering, a majority of the responses claim that the state is being mutated; as a result, Redux won't re-render. However, I'm using Lodash's merge to deep-dup an object, I'm pretty sure I'm not returning a modified object. (Please see attached snippet below)
Would love to hear some advice from you guys, pulling my hair out on this one!
const usersReducer = (state = {}, action) => {
Object.freeze(state); // avoid mutating state
console.log(state);
// returns an empty object
let newState = merge({}, state);
console.log(newState);
// returns my state with my dispatched action object inside already???
// newState for some reason already has new dispatched action
switch (action.type) {
case RECEIVE_USER:
let newUser = {[action.user.id] = action.user};
return merge(newUser, newUser);
case RECEIVE_USERS:
newState = {};
action.users.forEach(user => {
newState[user.id] = user;
});
return merge({}, newState);
default:
return state;
}
};
React Container Component
import { connect } from 'react-redux';
import { receiveUsers, receiveUser, refreshAll, requestUsers, requestUser } from '../../actions/user_actions';
import allUsers from '../../reducers/selectors';
import UserList from './user_list';
const mapStateToProps = (state) => ({
users: allUsers(state), // allUsers (selector that takes the state specfically the user Object and returns an array of user Objects)
state
});
const mapDispatchToProps = (dispatch) => ({
requestUser: () => dispatch(requestUser()),
requestUsers: () => dispatch(requestUsers()),
receiveUsers: (users) => dispatch(receiveUsers(users)),
receiveUser: (user) => dispatch(receiveUser(user)),
refreshAll: (users) => dispatch(refreshAll(users))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList);
React Presentational component
import React from 'react';
class UserList extends React.Component {
render() {
const { users, state } = this.props;
const userItems = users.map((user, idx) => {
return(<li key={idx}>{user.username}</li>);
});
return (
<div>
<ul>
{ userItems }
</ul>
</div>
);
}
}
export default UserList;
React Store
import { createStore, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import RootReducer from '../reducers/root_reducer';
const logger = createLogger();
const configureStore = (preloadedState = {}) => {
return createStore(
RootReducer,
preloadedState,
applyMiddleware(logger));
};
// const configureStore = createStore(rootReducer, applyMiddleware(logger));
// oddly enough, when I have the store as a constant and not a function that returns the store constant, dispatching actions through the terminal will correctly update the state and rerender the component
export default configureStore;
React Selector
const allUsers = ({ users }) => {
return Object.keys(users).map(id => (
users[id]
));
};
export default allUsers;
I had a similar problem, just in case someone stumbles upon this, I needed to clone the array in order to re-render the view:
export const addFieldRow = () => (
(dispatch: any, getState: any) => {
const state = getState();
const myArrayOfObjects = myArrayOfObjectsProp(state);
const newObject = {
key: "",
value: "",
};
myArrayOfObjects.push(newObject);
dispatch(addFieldRowAction({ myArrayOfObjects: [...myArrayOfObjects] })); <== here
}
);
Common problem in this case is using not reactive operations for changing state. For example use concat() for array, but not push() and so on.
I use this solution to do it.
I put the users on my state and update it on any change, with componentWillReceiveProps. Hope it helps :-)
class UserList extends React.Component {
constructor(props) {
super(props);
console.log(this.props);
this.state = {
users: props.users
};
}
componentWillReceiveProps(nextProps) {
if (this.props.users !== nextProps.users) {
this.setState({
users: nextProps.users,
});
}
}
render() {
const { users } = this.state;
const userItems = users.map((user, idx) => {
return(<li key={idx}>{user.username}</li>);
});
return (
<div>
<ul>
{ userItems }
</ul>
</div>
);
}
}
export default UserList;
What do your React components look like? Are you using internal state in them or props to push the data down. Usually I see the issue is with people setting the internal state of props with Redux state. You should be pushing props down to the components and they will re-render on update.
Also, check out
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
to see if the props are really changing.
Create new copy of array from the prop state to re-render the component
render() {
const {allPost} = this.props;
//Use the spread operator to create a new copy of the array
const posts = [...allPost];
const plansList = () => {
return posts.length < 1 ? null : posts && <PlansList allPost={posts}
/>;
};
return (
<>
<Container className="mt-lg-5 pt-lg-5">
{plansList()}
</Container>
</>
);
}
i spent lot of time to find out that when using more than 1 reducer (with combineReducers), then your mapStateToProps should point the correct reducer names, for example
const mapStateToProps = state => ({
someVar: state.yourReducerName.someVar,
loading: state.yourReducerName.loading,
error: state.yourReducerName.error
});

Resources