Reputation: 1405
Below is my component class. The component never seems to execute componentWillUpdate(), even when I can see the state updating by logging before the return in mapStateToProps. The state is 100% changing, however the component doesn't refresh.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { search } from './mapActions'
import L from 'leaflet'
class Map extends Component {
componentDidMount() {
L.Icon.Default.imagePath = './images'
this.map = new L.Map('map', {
center: new L.LatLng(this.props.lat, this.props.lng),
zoom: this.props.zoom,
layers: L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '<a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
})
})
}
componentWillUpdate() {
console.log('UPDATE MAP')
L.geoJson(this.props.data).addTo(this.map)
}
render() {
return <div id="map"></div>
}
}
const mapStateToProps = (state) => {
return {
isFetching: state.isFetching,
data: state.data
}
}
const mapDispatchToProps = (dispatch) => {
return {
search: (name) => {
dispatch(search(name))
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Map)
And here is the map reducer:
const initialState = {
isFetching: false,
data: {}
}
export const map = (state = initialState, action) => {
switch(action.type) {
case 'REQUEST_SEARCH_RESULTS':
return Object.assign({}, state, {
isFetching: true
})
case 'RECEIVE_SEARCH_RESULTS':
return Object.assign({}, state, {
isFetching: false,
data: action.data
})
default:
return state
}
}
After some more testing and logging it seems that when it goes to map state to props the state object it uses to map to props contains the correct data, so state.map.data is correct and I can see the return from the fetch. However when I then log this.props in componentWillUpdate(), the data object is there but empty.
Upvotes: 35
Views: 48586
Reputation: 20148
In my case I had same problem mentioned in the question and I have resolved it using new array create using spread operater instead of using array push method.
Error: rows.push(row)
const onAddRow = useCallback(rowIndex => {
const rows = props.managefunds
const row = rows.find((row, index) => {
return rowIndex === index
})
rows.push(row) // while directly push in to array, page is not refreshing
props.handleSubAccount({label: 'subAccount', value: rows })
}, [ props.managefunds ])
Solution: [ ...rows, row ]
const onAddRow = useCallback(rowIndex => {
const rows = props.managefunds
const row = rows.find((row, index) => {
return rowIndex === index
})
props.handleSubAccount({label: 'subAccount', value: [ ...rows, row ]}) // while create new array in the following way, the page is refreshing properly
}, [ props.managefunds ])
Upvotes: 0
Reputation: 179
Building on what Marina answered going from
var tempArray = imageArrayState
tempArray.push(5)
setImageArray(tempArray)
to
var tempArray = []
imageArrayState.foreach(value => {tempArray.push(value)})
tempArray.push(5)
setImageArray(tempArray)
made my app refresh
Upvotes: 0
Reputation: 1000
Because you're not changing the reference, so React's shallow compare doesn't detect the update.
I'm going to use a simple example with blog posts. In your reducer, you're probably doing something as follows:
case FETCH_NEW_POSTS
let posts = state.posts;
posts.push(action.payload.posts);
return {
...state,
posts
};
Instead of that, you must do something like the following:
case FETCH_NEW_POSTS
let posts = [...state.posts]; // we're destructuring `state.posts` inside of array, essentially assigning the elements to a new array.
posts.push(action.payload.posts);
return {
...state,
posts
};
Depending on your use case Object.assign() or lodash's clone/deepclone may be more idiomatic.
Upvotes: 31
Reputation: 1780
I had a similar problem and I found out the answer after read this:
Data gets set/updated/deleted in the store via the results of handling actions in reducers. Reducers receive the current state of a slice of your app, and expect to get new state back. One of the most common reasons that your components might not be re-rendering is that you're modifying the existing state in your reducer instead of returning a new copy of state with the necessary changes (check out the Troubleshooting section). When you mutate the existing state directly, Redux doesn't detect a difference in state and won't notify your components that the store has changed. So I'd definitely check out your reducers and make sure you're not mutating existing state. Hope that helps! (https://github.com/reactjs/redux/issues/585)
When I tried use Object.assign({}, object)
as you, it didn't work anyway. So was when I found this:
Object.assign only makes shallow copies. (https://scotch.io/bar-talk/copying-objects-in-javascript)
Then I understood that I had to do this: JSON.parse(JSON.stringify(object))
or just this:
{...object}
For Arrays:
[...theArray]
I hope that this will help you
Upvotes: 44
Reputation: 71
Make sure you are listening to that store in your maps file if you are passing props to your component through maps file.
export const listenToStores = [CommonStore, store];
@connection(maps.listenToStores, maps.getStateFromStores)
Upvotes: 0
Reputation: 39222
componentWillUpdate receives the incoming props as an argument. At this time, this.props
is still the old props. Try changing your method like so:
void componentWillUpdate(nextProps, nextState) {
L.geoJson(nextProps.data).addTo(this.map);
}
Upvotes: 8