0

Even though printing items logs a populated array before the return function, it doesnt really render anything. I know for a fact its not a problem with improperly displaying the html. So i got suspicious and stringified it inside the return function to see if indeed the data im logging is there and to my dread i realised it isnt. As shown in the code, within the return function i get an empty array!

class Derp extends Component {

    constructor(props){
        super(props);

        mainStore.subscribe(this.render.bind(this));
    }

    render(){
        var items = mainStore.getState().itemReducer.items;

        console.log(items); //yields an array of items as expected

        return (
            <div>
                <span>{JSON.stringify(items)} </span> //yields [] in the DOM !!!!!!!
                //when it should yield the same as above, a fully populated array
                {
                items.map(item =>
                <div key={item.id}>
                     {item.name}
                </div>
                )
                }
            </div>
            )
    }

}

I've done this numerous times succesfully but this time around i just cant figure out what could be wrong with it.. Thanks for taking the time.

EDIT 1: I know this will seem cringeworthy ( because it is ) but the component is listening to all state changes like so : mainStore.subscribe(this.render.bind(this)); so it should always have access to updated data.

P.S: I am aware of dumb vs clever components and that im not using ReactRedux, im just experimenting and trying a few different things for curiosity's shake. This is an self-imposed "study" kind of code. This isnt meant for production or a project.

Return-1
  • 2,329
  • 3
  • 21
  • 56
  • I am very surprised this does not throw an error. You don't have your tags enclosed by a parent html tag container. You have a list of a span and divs. You need to wrap those in one big div. – Andrew Nov 21 '17 at 21:18
  • i did, i just forgot to add it originally to this code as this is a cleaned up version. i edited it to have it but this isnt whats the weird behaviour here, its that im logging it on line 3 and it displays a fully populated array but stringifying it on line 6 gives me an empty array! – Return-1 Nov 21 '17 at 21:25
  • It looks like you're using redux. I think behavior would be more predictable if you connected the component to redux and added the `items` to `mapStateToProps`. This is convention, and imo you should be getting used to doing it this way anyways. Can you try that instead and report back? – Andrew Nov 21 '17 at 21:31
  • im pretty sure that would work. Passing the data as a prop would probably also work too. Im just trying to figure out why this doesnt. logic says it should and it'd be a pretty big defeat not figuring out why it doesnt.. – Return-1 Nov 21 '17 at 21:32
  • This might be something you bring up in their github issues. It's hard to "reason out" a library that abstracts so many things from the user – Andrew Nov 21 '17 at 22:20
  • it turns out the issue was with calling render directly. maybe i should take a closer look at the inner workings of react to see what it is that render actually does. – Return-1 Nov 22 '17 at 08:16

4 Answers4

0

Return the div from your map function:

items.map(item =>
    return <div key={item.id}>
        item.name
    </div>
)
sma
  • 9,449
  • 8
  • 51
  • 80
  • Unfortunately thats not the problem itself. Im using the same syntax a few blocks futher down and its all fine. Even the same block of jsx works when i re-visit the controller a second time for some reason. What is beyond me is that logging the items array clearly displays there's content there but when logging it from inside the return method it shows up as empty! – Return-1 Nov 21 '17 at 21:05
0

Try escaping the content you wish to render:

items.map(item =>
  <div key={item.id}>{item.name}</div>
)
Donny West
  • 710
  • 3
  • 6
  • Everything renders fine the second time i get to the controller. The problem is, even though im logging the items properly and i can see there's data there as shown in my example, the mapping doesnt happen. {JSON.stringify(items)} //yields [] in the DOM even though it logged a FULLY populated array ONE line of code beforehand... and this is all synchronous code we re speaking of here.... – Return-1 Nov 21 '17 at 21:13
  • Perhaps something goofy is happening during the stringify operation? Does your code work without stringifying? – Donny West Nov 21 '17 at 21:17
  • no. i just added the stringify because i had a hunch that inside the return statement there's just no data and to my dread the hunch was correct.. i mean logging it and stringifying it is like... two lines apart. i am stunned – Return-1 Nov 21 '17 at 21:24
0

There are two problems I see with your code:

  1. You are trying to return more than one element. If you're before react16, you need to wrap those two elements in a div or something. If you're on react16, you can return them in an array.
  2. You item.name needs to be in curly braces. Any JS within JSX markup needs to have curly braces. (This is how they know it's JS and not markup).

react16+

    return [
        <span>{JSON.stringify(items)} </span>,
        ...items.map(item =>
           <div key={item.id}>
               {item.name}
           </div>
        )
    ]

< react16

    return (
        <div>
            <span>{JSON.stringify(items)} </span>
            {items.map(item =>
                <div key={item.id}>
                    {item.name}
                </div>
            )}
        </div>
    )
  • they are returned in one element, i just forgot to add it to the code here. I am trying to stress that the problem isnt the rendering itself, it renders just fine the second time i return to the controller, for some reason. As mentioned in both the comments below unfortunately what seems to be the problem is that logging the array seems to be populated before the return statement and not populated inside it... – Return-1 Nov 21 '17 at 21:22
  • Ah okay well console.logs do happen asynchronously. If you look next to the value in the console, it will probably have a little purple "i" that says "evaluated just now" or something similar. It sounds like your 'items' variable isn't populated on the first go-around. –  Nov 21 '17 at 21:28
  • Yeah i was afraid of this being a case of "dont trust console.log" but im not sure how to approach this to see whether the data is actually there or not. On top of that, it should really matter as the class is subscribed to the store in the following way : mainStore.subscribe(this.render.bind(this)); And should therefore render when the new data has come in.. So.. no idea – Return-1 Nov 21 '17 at 21:31
  • Check out the top answer here for some tricks you can try: https://stackoverflow.com/questions/23429203/weird-behavior-with-objects-console-log. Other than that, there may be a problem with the way you're subscribing to the store. Have you considered using react-redux to make that a little easier? –  Nov 21 '17 at 21:33
  • Im pretty sure it would work with react-redux as well as by just simply passing the props to the component directly as things usually happen for components like this. Im just a bit hellbent on trying to understand why it is that this doesnt work instead as this is mostly the point of this "excercise" im putting myself through. – Return-1 Nov 21 '17 at 21:35
  • Have you tried putting `items` into the component's local state prior to rendering, like in `componentDidMount`? Then consume/map over `this.state.items` in your render method? That might squash any async issues you're bumping up against. – Donny West Nov 21 '17 at 21:44
0

It seems like the problem was calling render directly. Instead calling forceUpdate() works fine. I am not 100% why this happens but i suspect that calling render on it's own doesnt mean much as it would probably need to be called in a React context in the React pipeline. I might be horribly off and if so please flag me and describe it a little better that i currently am able to.

Thanks everyone for helping.

Return-1
  • 2,329
  • 3
  • 21
  • 56