D_S D_S - 24 days ago 10
React JSX Question

Hiding and showing text in React

Hey guys this is something very stupid, but I'm having troubles wrapping ly head around. I'm trying to show/hide text inside one of my components, but I'm not able to do it. I get

I was clicked!
message so I know the function is being passed down. What am I missing? Do I also need to declare a
visibility
css declaration, maybe that's what I'm missing. I'm getting cornfused. :/

Thanks in advance:

SnippetList.jsx

import React, { Component, PropTypes } from 'react'
import { createContainer } from 'meteor/react-meteor-data';
import Snippet from './snippet'
import { Snippets } from '../../../api/collections/snippets.js'


class SnippetList extends React.Component {
constructor(props) {
super(props);
this.state = { visible: false }
this.toggleVisible = this.toggleVisible.bind(this);
}

toggleVisible() {
this.setState( { visible: !this.state.visible } )
console.log('I was clicked');
}

renderSnippets() {

return this.props.snippets.map( (snippet) => (
<Snippet
key={snippet._id}
title={snippet.title}
content={snippet.content}
onClick={this.toggleVisible}
/>
));
}

render() {
const snippets = Snippets.find({}).fetch({});

return (
snippets.length > 0
?
<ul>{this.renderSnippets()}</ul>
:
<p>No Snippets at this time</p>
)
}
}

SnippetList.propTypes = {
snippets: PropTypes.array.isRequired,
}

export default createContainer(() => {
Meteor.subscribe('snippets');

return {
snippets: Snippets.find({}).fetch()
};
}, SnippetList);


Snippet.jsx

import React, { Component, PropTypes } from 'react'

export default class Snippet extends React.Component {

render() {
const visible = this.props.toggleVisible
return (
<article>
<header>
<h1 className='Snippet-title'>{this.props.title}</h1>
</header>
<div className={visible ? 'show' : 'hidden'} onClick={this.props.onClick}>
<p className='Snippet-content'>{this.props.content}</p>
</div>
</article>
)
}
}

Snippet.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
// toggleVisible: PropTypes.func.isRequired
}

Answer

the issue is you aren't passing the hide part as a prop.

in Snippet you do const visible = this.props.toggleVisible but... toggleVisible isn't passed to your Snippet component thus its always undefined

return this.props.snippets.map( (snippet) => (
    <Snippet 
    key={snippet._id} 
    title={snippet.title}
    content={snippet.content}
    onClick={this.toggleVisible}
    />
));

add toggleVisible... aka change to this.

return this.props.snippets.map( (snippet) => (
    <Snippet 
        key={snippet._id} 
        title={snippet.title}
        content={snippet.content}
        toggleVisible={this.state.visible}
        onClick={this.toggleVisible}
    />
));

you should probably also bind your renderSnippets this to the class as well... meaning add this to your constructor this.renderSnippets = this.renderSnippets.bind(this);


Now to talk about your code, why are you rendering a <ul> as the parent of a <article> ? the child of a ul should be a <li> I would refactor your components to be more like this.

class SnippetList extends React.Component {
    constructor(props) {
        super(props);
        this.state = { visible: false };
        this.toggleVisible = this.toggleVisible.bind(this);
        this.renderSnippets = this.renderSnippets.bind(this);
    }

    toggleVisible() {
        this.setState( { visible: !this.state.visible } )
        console.log('I was clicked');
    }

    renderSnippets() {
        return this.props.snippets.map( (snippet) => (
            <Snippet 
                key={snippet._id} 
                title={snippet.title}
                content={snippet.content}
                toggleVisible={this.state.visible}
                onClick={this.toggleVisible}
            />
        ));
    }

    render() {
        const snippets = Snippets.find({}).fetch({});
        return (
            snippets.length > 0 
              ?  <ul>{this.renderSnippets()}</ul>
              :  <p>No Snippets at this time</p>
        )
    }   
}


export default class Snippet extends React.Component {
    render() {
        const {toggleVisible: visible} = this.props;
        return (
            <li>
                <article>
                    <header>
                        <h1 className="Snippet-title">{this.props.title}</h1>
                    </header>
                    <div onClick={this.props.onClick}>
                        <p className={visible ? 'show Snippet-content' : 'hidden Snippet-content'}>{this.props.content}</p>
                    </div>
                </article>
            </li>
        )
    }
}
Comments