0

React is not mapping through an array. I can see an array in the console log like this.Shows loading instead of mapping through the array.

enter image description here

The array comes from redux reducer. I think im doing everything right according to redux.

reducer/posts.js

import {GET_POSTS, '../actions/';

const initialState = {
    post: [],
    postError: null,
    posts:[]
}

export default (state = initialState, action) => {
    switch (action.type) {
        case GET_POSTS:
            // console.log(action.data)
            return {...state, posts: action.data}
    default:
            return state

Could it be an async lifecycle method issue ?

actions/index.js

export const GetPosts = () => {
    return (dispatch, getState) => {
        return Axios.get('/api/posts/myPosts')
            .then( (res) => {
                 const data = [...res.data]

                 console.log(data); // logs data and i can see an array 

                 dispatch({type: GET_POSTS, data })
             })

    }
}

Posts.js

import React, { Component } from 'react';
import PostList from './PostList';
import Axios from '../Axios';
import {connect} from 'react-redux';
import { withRouter, Redirect} from 'react-router-dom';
import {DeletePost, GetPosts} from '../actions/';

const Styles = {
    myPaper:{
      margin: '20px 0px',
      padding:'20px'
    }
    , 
    wrapper:{
      padding:'0px 60px'
    }
}
class Posts extends Component {


  constructor(props){
    super(props);

    this.state = {
      posts: [],
      loading: true,
    }
  }


  componentWillMount(){
    this.props.GetPosts();
    const reduxPosts = this.props.myPosts;
    const ourPosts = reduxPosts  
    console.log(reduxPosts); // shows posts line 35
  }

  onDelete = (id) => {
    Axios.post(`/api/posts/delete/${id}`);
    this.setState({
      posts: this.state.posts.filter(post => post.id !== id)
    })
  }


  render() {
    const {loading} = this.state;
    const { myPosts} = this.props
    if (!this.props.isAuthenticated) {
      return (<Redirect to='/signIn' />);
    }
    if(loading){
      return "loading..."
    }
    return (
      <div className="App" style={Styles.wrapper}>
        <h1> Posts </h1>
        {/* doesn't map posts instead shows loading */}
        <PostList posts={myPosts}/>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  isAuthenticated: state.user.isAuthenticated,
  myPosts: state.post.posts
})
const mapDispatchToProps = (dispatch, state) => ({
  // newPost: (post) => dispatch(newPost(post)),
  // DeletePost: (id) => dispatch( DeletePost(id))
  GetPosts: () => dispatch( GetPosts())
});
export default withRouter(connect(mapStateToProps,mapDispatchToProps)(Posts));

PostList.js

import React from 'react';
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import moment from 'moment';
import {connect} from 'react-redux';
import {DeletePost} from '../actions/';
const Styles = {
  myPaper:{
    margin: '20px 0px',
    padding:'20px'
  }
}
const PostList = ({ DeletePost, posts}) =>  {
  return(
    <div>
    {posts.map( (post, i) => (
      <Paper key={i} style={Styles.myPaper}>
        <Typography  variant="h6" component="h3">
        {post.title}  
        </Typography>
        <Typography component="p">
          {post.post_content}
          <h5> by: {post.username}</h5>
          <h5> {moment(post.createdAt).calendar()}</h5>
        </Typography>
        <Button 
          variant="outlined" 
          color="primary" 
          type="submit"
          onClick={() => DeletePost(post.id)}>
          Remove
        </Button>
      </Paper>
     ))}
  </div>
  )
  };
export default PostList;
3
  • in your actions/index.js change dispatch({type: GET_POSTS, data }) to ` dispatch({type: GET_POSTS, data: data }) and see if it works. generally all requests should be async in nature and hence as answered, you should use a middleware like thunk Commented Apr 13, 2019 at 20:00
  • ok let me try that. Commented Apr 13, 2019 at 20:04
  • Where are you updating the loading variable? If loading is complete Commented Apr 13, 2019 at 20:06

1 Answer 1

1

There doesn't seem to be anywhere in your code where you set loading to false when the getPosts function has resolved - so your app will always return the "loading..." string. Try something like this (assuming GetPosts returns a promise):

  async componentWillMount(){
    await this.props.GetPosts();
    this.setState({ loading: false })
    const reduxPosts = this.props.myPosts;  
    console.log(reduxPosts); // shows posts line 35
  }
Sign up to request clarification or add additional context in comments.

2 Comments

this work, why does switching it to async make it work ?
you make it async so that when its done, you can call setState({loading:false})

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.