monsterpiece
monsterpiece

Reputation: 779

Render array of objects from Redux store as React components

I'm trying to display my initial state from my store. I know my code is not correct but i'm hoping to learn the most simple method for this. Can I simply receive data from the store with props? or do I need some lifecycle event to target data in the store?

Here is my current attempt: I have edited this to include my reducer and I have updated my component as per the comments below.


//store .js

import { createStore, applyMiddleware,compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; //the index.js file

const initialState = {

}

const middleware = [thunk]

const store = createStore(
    rootReducer, 
    initialState, 
    compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__&& window.__REDUX_DEVTOOLS_EXTENSION__()
)
);

export default store;

Now when trying to map out my props in the below component, I get the error:

this.props.properties.map is not a function

//Properties component that renders a map of my props

import React, { Component } from 'react'
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {getListings} from '../actions/postActions';

 class Properties extends Component {

    componentDidMount(){
        getListings()
    }

    render() {
        const properties = this.props.properties.map(property => (
            <div key = {property.id}>
                <h3>{property.propertyName}</h3>
                <div style={{width: '4em', height:'4em', backgroundColor: 'blue'}}>image</div>
                <p>{property.footage}</p>
                <p>{property.address}</p>
                <p>{property.price}</p>
            </div>
        ))
        return (
            <div>
                <h1>Current Listings</h1>
                    {properties}
            </div> 
        )
    }
}
Properties.propTypes = {
    properties: PropTypes.array.isRequired,
    newListing: PropTypes.object
}

const mapStateToProps = state =>({
    properties: state.properties.properties,
    newListing: state.properties.newListing
});
export default connect(mapStateToProps)(Properties)

//my actionsHandler.js

import {GET_LISTINGS, NEW_LISTING} from './types';


export function newProperty(formdata){
    return function(dispatch){
        dispatch({
            type: NEW_LISTING,
            payload: formdata
        })
    }
}

export function getListings(form){
    return function(dispatch){
        dispatch({
            type: GET_LISTINGS,
        })
    }
}
//my reducer
import {NEW_LISTING, GET_LISTINGS} from '../actions/types';

const initialState={
    properties: [
        {
            id: 1,
            propertyName: 'The boogaloo',
            footage: '3500 sqft',
            address: '123 hill bounty rd',
            price:'$ 350,000.00'
        }
    ],
    newListing: {}
}

export default function(state=initialState, action){
    switch(action.type){

        case NEW_LISTING:
            return{
                ...state,
                properties:state.properties.push(action.payload)
            }
        case GET_LISTINGS:
            return{
                state
            }

        default:
            return state
    }
}

Upvotes: 1

Views: 2869

Answers (3)

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15530

Your general approach is viable in general, with certain corrections required though:

  • your state is expected to be an object (not an array as it is currently)
  • in order for your entire array of objects to appear as a bunch of React elements, you may want to wrap them into some parent component and map() your individual posts as small components that receive corresponding object as props from parent component

You may inquiry the following live-demo as a reference:

//dependencies
const { render } = ReactDOM,
      { createStore } = Redux,
      { connect, Provider } = ReactRedux
      
//initial state, doomy reducer and basic store (no middleware)
const initialState = {posts:[{id:'0',propertyName:'house',footage:'1800 sqft',address:'108 boogaloo dr. thisplace, CA',price:'$145,300.00'},{id:'1',propertyName:'closet',footage:'110 sqft',address:'23 dirt dr. badplace, NC',price:'$3'},{id:'2',propertyName:'garage',footage:'1000 sqft',address:'10 meadow st. decent, NY',price:'$100,000.00'},{id:'3',propertyName:'fishing shack',footage:'500 sqft',address:'108 fishy dr. forestgrove, NJ',price:'$200,300.00'},{id:'4',propertyName:'huge mansion',footage:'8000 sqft',address:'32 T-Pain st. onlytwentythree, WI',price:'$100.00'},{id:'5',propertyName:'dog house',footage:'200 sqft',address:'2367 goodboy dr. borks, KA',price:'$1000.00'},{id:'6',propertyName:'beehive',footage:'too big to measure',address:'108 stingya dr. Bidibi, NJ',price:'$300.00'},{id:'7',propertyName:'my house',footage:'4000 sqft',address:'42 treeface dr. bigwoods, FL',price:'$190,300.00'},]},
      appReducer = (state=initialState, action) => state,
      store = createStore(appReducer)
//post ui component
const Post = ({propertyName, footage, address, price}) => (
  <div>
    <h3>{propertyName}</h3>
    <p>footage: {footage}</p>
    <p>address: {address}</p>
    <p>price: {price}</p>
  </div>
)
//wrapper ui component relying on storedPosts property
//that will get connected to global state posts later on
const PostBoard = ({storedPosts}) => (
  <div>
    {storedPosts.map((post, key) => <Post key={key} {...post} />)}
  </div>
)
//connect storedPosts to global state posts
const mapStateToProps = ({posts}) => ({storedPosts: posts}),
      PostBoardComponent = connect(mapStateToProps)(PostBoard)
//render Redux Provider
render (
  <Provider store={store}>
    <PostBoardComponent />
  </Provider>,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.3/react-redux.min.js"></script><div id="root"></div>

Upvotes: 1

Shashidhar Reddy
Shashidhar Reddy

Reputation: 195

In redux, you will be defining an initial state in reducer and will do some updation or removal of the data from the state using actions and will store i.e. you are updating the object the initial state object will be updated..In few cases you need the initial state directly. So,you need to make a copy of initial state object and perform the tasks on it. When the new object updated the initial state will not have any effect.

You can update using components by directly dispatching an action from the component i.e. mapdispatchtoProps.

You can access the entire store data in component by connecting it i.e. connect(mapStatetoProps, mapDispacthtoProps).

mapSatettoProps will give you store data as state. You can modify it or you can do whatever you want. state access can be done in component also but you will get updated one.

Upvotes: 1

Ajay Ghosh
Ajay Ghosh

Reputation: 439

use a lifecycle method for updating the component

like shouldComponentUpdate method,

props change Doesn't cause rerender only state change cause rerender, is such case store the props as state is also a solution (inappropriate)

Upvotes: 2

Related Questions