Reputation: 3545
I'm quite new to Redux, and I'm trying to dispatch and async action and return the new state as updated by the response data.
Here is where I'm at:
App.js (container component)
import React, { Component } from 'react'
import { connect } from 'react-redux'
// import all actionCreators
import * as actions from '../actions/actionCreators'
// import component to pass props to
import Main from './Main'
const mapStateToProps = (state, { params, location, users }) => {
const rootPage = params.rootPage || '';
return {
users,
rootPage
}
}
const App = connect(
mapStateToProps,
actions
)(Main);
export default App;
Action Creator
export const fetchRemoteData = (endpoint) => (dispatch, getState) => {
return axios.get('https://jsonplaceholder.typicode.com/users').then(
response =>
dispatch({
type: 'FETCH_REMOTE_DATA_SUCCESS',
endpoint,
response
})
)
}
Component
export default class Users extends Component {
componentDidMount() {
console.log('Component Mounted!')
this.props.fetchRemoteData('users')
}
render() {
const {users} = this.props
console.log(users)
const {rootPage} = this.props;
return (
<div>
<h1>Users Page</h1>
<p>This is the Users page and an example of how your app is working correctly</p>
<p>rootPage: {rootPage}</p>
{/*users.map(user =>
<p>{user.name}</p>
)*/}
</div>
)
}
}
When I look in React Dev Tools, the users
object isn't there, even though it IS in my global state object.
This made me think I maybe haven't mapped it to my container component correctly, but I can't see what's wrong with this either...
Root Reducer
// import individual reducers
import users from './users'
const rootReducer = combineReducers({
users,
routing: routerReducer
})
export default rootReducer
Is it something to do with the fact that my API call is made in componentDidMount
, and my component hasn't registered the change made to the redux state after the response has been received?
Any help appreciated.
I forgot to include users: state.users
in my mapStateToProps
function. Now the users
object is in my props, but it still doesn't contain the state...
Main.js
import React, { Component } from 'react';
import { Link } from 'react-router'
export default class Main extends Component {
render() {
return (
<div className='container'>
<h2>Main.js (wrapped by App.js)</h2>
<hr />
<Link to='/'>Home</Link>
{' | '}
<Link to='/users'>Users</Link>
{' | '}
<Link to='/posts'>Posts</Link>
<hr />
{React.cloneElement(this.props.children, this.props)}
</div>
)
}
}
Note: I'm rendering Users
and Posts
as children of react router's Router
component.
Ok, so the action is definitely reaching the reducer, and the reducer is definitely updating the redux store.
I now have it so that after the action is dispatched, the Main
component's props are updated as desired, after the Users
component has been mounted.
So, my problem is almost resolved. I think the issue now is one concerning componentDidMount
. The response payload is making it's way to Main
component props, but when I try to render the response the data, I get nothing.
Users
render function:
{this.props.users ?
this.props.users.map(user => <p>{user.name}</p>) :
<p>No data</p>
}
All I'm getting is 'No data', and I don't know how to get the data returned in componentDidMount
into my view.
I hope that's a bit clearer - apologies if not.
For the sake of full disclosure, here's my reducer:
const users = (state = [], action) => {
switch(action.type) {
case 'FETCH_REMOTE_DATA_SUCCESS' :
if (action.response) {
return action.response.data
}
default :
return state;
}
return state;
}
export default users;
Upvotes: 2
Views: 6551
Reputation: 20614
If you see the correct data coming through mapStateToProps
then you should see the correct data in Main
, which means passing those props to Users
should also work.
const mapStateToProps = (state, { params }) => {
console.log(state.users) <--- make sure this is correct
const rootPage = params.rootPage || '';
return {
users: state.users,
rootPage
}
}
Then in Main:
render() {
console.log(this.props.users) <--- make sure this is correct
return (
...
)
}
If the above two steps log the incorrect data then something is up with reducers / actions (sounds like it's not, so good).
If those are correct and you're NOT getting the right data in Users component then the problem is with this line:
{React.cloneElement(this.props.children, this.props)}
I think you might need to write that bit a differently, (not 100% sure though). I do a more comprehensive copy of props to my child routes
let children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
...child.props,
...this.props,
})
})
Alternatively, you can connect
your Users component which is probably a good idea anyways because it's the one that's actually using users
connect(state => ({ users: state.users }))(Users)
Upvotes: 2