I am struggling to find the right way to access the data in my react component.

This is my action creator. I save every row returned from the promise into an array

export function getBlocks(){
console.log("get blocks called");

var blocks = [];
eos.getTableRows(true, "blockboard", "blockboard", 
"board").then(function(data){
console.log("data in callback",data);

for(var i = 0; i < data.rows.length; i++){
  blocks.push(data.rows[i]);
}

});

  console.log("the blocks",blocks);

  return {
    type: GET_BLOCKS,
    payload: blocks
  };
}

This is my reducer:

export default function (state = [], action){
console.log('Action received', action);

    switch(action.type){
     case GET_BLOCKS:
        console.log("the received action", action.payload);
        return [...state, action.payload];
    }

return state;
}

In the component I receive the data but when I try to display it, it doesn't work. It gives me undefined when trying to access a specific row in the array.

import React, {Component} from 'react';
import {connect} from 'react-redux';

class Blocks extends Component {

renderBlocks(blockData){

 console.log("Data in renderBlocks Func",blockData);
 const alldata = blockData[0];
 console.log(alldata);


 if(alldata !== undefined){
   console.log("alldata ", alldata);
   return alldata.map(function(data){
     return(
       <div key={data.id}>
         <h3>{data.text}</h3>
       </div>);
   });

   }


   }

  render(){
   return (
      <div>
        <h2>Helloooo</h2>
        {this.renderBlocks(this.props.blocks)}
        <div></div>
      </div>
    );
  }
}


function mapStateToProps({blocks}){
  return {blocks};
}
export default connect(mapStateToProps, null)(Blocks);

These are the values for the console.logs that I get in the renderBlocs function.

console.logs in renderBlock Function

If I do something like blockData[0][0] to access the data, I get a "Uncaught TypeError: Cannot read property '0' of undefined"

EDIT: 15.9.18 - 14:00 european time

I updated the renderBlocks function to reflect the answer.

renderBlocks(blockData){
console.log("Data that renderBlocks receives, blockData",blockData);
var alldata = blockData[0];
console.log("blockData[0]",alldata);


if(alldata !== undefined){
  const newdata = Object.keys(alldata).map(function(k){
    return alldata[k];
  });
  console.log("the new array looks like this", newdata);

  console.log(alldata);
}
}

This are the returned data objects: data in the browser

I found a solution with the help of TeeAttack42. Thank you very much for your help!

Solution

First I updated my action creator and reducers to reflect TeeAttack42s submission:

action creator:

export const getBlocks = async () => {
console.log("get blocks called");

let blocks = [];
await eos.getTableRows(true, "blockboard", "blockboard", 
"board").then(function(data){
    console.log("data in callback",data);
    if(data){
      blocks = data.rows;
    }


});

  console.log("the blocks",blocks);

  return {
    type: GET_BLOCKS,
    payload: blocks
  };
} 

reducer:

export default function (state = [], action){
  console.log('Action received', action);

  switch(action.type){
    case GET_BLOCKS:
    console.log("the received action", action.payload);
    return [...state, ...action.payload];
  }

  return state;
}

Then I could do this in my component and it displayed the text:

  renderBlocks(blocks){
    if(blocks.length > 0){
      return blocks.map(function(data){
        return (<div key={data.id}>
                  <h3>{data.text}</h3>
                </div>);
      });
    }
  }

  render(){
    const {blocks} = this.props;
    console.log(blocks);

    return (
      <div>
        <h2>Helloooo</h2>
        {this.renderBlocks(blocks)}
      </div>
    );
  }

summary:

I think that earlier approaches failed because of the async behaviour. This discussion helped me to understand https://stackoverflow.com/questions/17546953/cant-access-object-property-even-though-it-exists-returns-undefined. I was confused because console.log showed me something that maybe wasn't even there when I called it.

The output of console.log(anObject) is misleading; the state of the object displayed is only resolved when you expand the > in the console. It is not the state of the object when you console.log'd the object.

By checking the blocks.length I make sure that the props.blocks is reiceived, then I can go on to show the content.

up vote 1 down vote accepted

EDIT:

Actually your blocks are the data.rows so you can copy it. Also the getTableRows function is an async call. That's why we add async and await which will handle the method synchronosly.

export const getBlocks = async () => {
  console.log("get blocks called");

  let blocks = [];
  await eos.getTableRows(true, "blockboard", "blockboard", 
    "board").then(data => {
               console.log("data in callback", data);
               if(data){
                 blocks = data.rows;    
               }
             });

  console.log("the blocks",blocks);    
  return {
    type: GET_BLOCKS,
    payload: blocks
  };
}

Your reducer should copy the action.payload content, therefore use also a spread operator on it.

export default const receiveBlocks = (state = [], action) => {
    console.log('Action received', action);    
    switch(action.type){
     case GET_BLOCKS:
        console.log("the received action", action.payload);
        return [...state, ...action.payload];
    }    
    return state;
}

This uses an arrow function for rendering each Block as a component. I also fixed the object structure so that you can map over the props.blocks element easier.

import React, {Component} from 'react';
import {connect} from 'react-redux';

class Blocks extends Component {

  render(){
   const {blocks} = this.props;
   const Block = {data} =>  <div key={data.id}>
             <h3>{data.text}</h3>
          </div>)

   return (
      <div>
        <h2>Helloooo</h2>
        {blocks.map(item => <Block data={item}/>)}
        <div></div>
      </div>
    );
  }
}


function mapStateToProps({blocks}){
  return {blocks: ...blocks};
}
export default connect(mapStateToProps, null)(Blocks);

Obsolete - Just if alldata would be an object and not an array:

I guess the problem lies in your object structure. Try to convert the object to a real array and not a JSON object with number values.

Object.keys(alldata).map(function(k) { return alldata[k] });

This will give you an array.

Another option could be to access the data by using a string and not a number like:

alldata["0"]
  • I also think the problem lies within my object structure but this solution doesn't work. If I use the Object().map function on blockData it gives me an array where the first position is an array with the three elements. If I use this on blockData[0] it gives me an empty array. Using blockData["0"]["0"] also returns undefined. – bhikkhu Sep 14 at 13:24
  • Did you use the Object.keys(alldata) method? Because you want to iterate over the keys and not the object itself. Also you should try alldata = blockdata[0] and alldata["0"] or blockdata[0]["0"] because blockdata is an array and not an object. Therefore using a string as the first parameter won't work. – TeeAttack42 Sep 14 at 16:08
  • I used this: if(alldata !== undefined){ const newdata = Object.keys(alldata).map(function(k){ return alldata[k]; }); – bhikkhu Sep 15 at 11:38
  • edited my question to reflect your answer – bhikkhu Sep 15 at 12:02
  • Hmm ok, I just saw that alldata seems to be a simple a array and not an object. Sorry for that. What could be is that the map function does not work as expected... I will update my answer for a new possible solution. – TeeAttack42 Sep 15 at 14:22

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.