4

I'm new to using react and have run into the issue stated:

Uncaught TypeError: Cannot read property 'setState' of null.

Basically what I am trying to do is, a user can click on three different headlines, and on click, it will render a certain template specific for that headline. This is the code I'm currently using that gave me this error:

class Selection extends React.Component {

constructor(props) {
    super(props);
    this.state = {selections: [], donateActive: true, fundraiseActive: false, speakActive: false };
}

componentDidMount() {
    this.setState({
        selections: selectionData.selections
    })
}

componentWillUnMount(){
    console.log("unmounted!"); 
}

donateOnClick() {
    this.setState({ donateActive: true, fundraiseActive: false, speakActive: false});
}

fundraiseOnClick() {
    this.setState({ fundraiseActive: true, donateActive: false, speakActive: false});
}

speakOnClick() {
    this.setState({ speakActive: true, fundraiseActive: false, donateActive: false});
}

donateTemplate() {
    return (
        <div>
            <h1>donate template</h1>
        </div>
    )
}   

fundraiseTemplate() {
    return (
        <div>
            <h1>fundraise template</h1>
        </div>
    )

}

speakTemplate() {
    return (
        <div>
            <h1>speak template</h1>
        </div>
    )
}

render() {
    return(
        <div>
            <div className="col-sm-6 col-sm-push-6 right">
                <div className="right-content-container">
                    <div className="selections">
                        <h3>You can</h3>
                        <h1 onClick={this.donateOnClick} className="selection active" id="selection-donate">donate</h1>
                        <h1 onClick={this.fundraiseOnClick} className="selection" id="selection-fundraise">fundraise</h1>
                        <h1 onClick={this.speakOnClick} className="selection" id="selection-speak">speak up</h1>
                        <hr></hr>
                    </div>
                    <div>
                        {
                            if (this.state.donateActive) {
                                this.donateTemplate()
                            }
                            if (this.state.fundraiseActive) {
                                this.fundraiseTemplate()
                            }
                            if (this.state.speakActive) {
                                this.speakTemplate()
                            }
                        }
                    </div>
                </div>
            </div>
            <div className="col-sm-6 col-sm-pull-6 left">
                <div className="left-content-container">
                    <img className="img-responsive hidden-xs" src="./images/selections/.jpg" alt=""/>
                    <img className="img-responsive hidden-sm hidden-md hidden-lg" src="./images/selections/.jpg" alt=""/>
                </div>
            </div>
        </div>
    );
}


}

Additionally, I'm not sure if I am able to use IF statements inside of return() the way I am doing right now inside of the render() method, is there a better way to do this?

Any help would be greatly appreciated.

dirtydanee
  • 6,081
  • 2
  • 27
  • 43
usednapkin
  • 37
  • 1
  • 3
  • Possible duplicate of [Unable to access React instance (this) inside event handler](http://stackoverflow.com/questions/29577977/unable-to-access-react-instance-this-inside-event-handler) – Davin Tryon Dec 22 '16 at 16:39

2 Answers2

7

Your problem relates to this binding. React doesn't automatically bind the React component to the value of this, except for the constructor and lifecycle methods. To fix this, in your constructor, add this for each function you want to bind this for

constructor(props) {
  super(props);
  this.donateOnClick = this.donateOnClick.bind(this);
  // other function bindings
}

As for your if statement question, as long as the if statements are wrapped in {}, you can use them. The curly braces indicate to the JSX parser that anything inside of them should be evaluated as javascript, so any valid javascript can be placed inside of them.

UPDATE: Not sure why those if statements are throwing an Unexpected token error, but try this:

<div>
    { this.state.donateActive ? this.donateTemplate() : null }
    { this.state.fundraiseActive ? this.fundraiserTemplate() : null }
    { this.state.speakactive ? this.speakTemplate() : null }
</div>

That's using the ternary if statement. If you're unfamiliar with the syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator

If that doesn't work, please update your question with a more detailed error message. In particular, I'd be curious to see what token it's saying is unexpected

taylorc93
  • 3,676
  • 2
  • 20
  • 34
  • Is the current way I have the three if statements wrapped in {} wrong? I get a syntax error: "SyntaxError: Unexpected token" – usednapkin Dec 22 '16 at 16:35
  • using ternary statements work!, I'm wondering if it's because I have to wrap each line of the if statements written the other way in {} in order for it to work – usednapkin Dec 22 '16 at 16:55
1

You can also autobind your methods in your class if you declare them like this:

donateOnClick = () => {
    this.setState({ donateActive: true, fundraiseActive: false, speakActive: false});
}

fundraiseOnClick = () => {
    this.setState({ fundraiseActive: true, donateActive: false, speakActive: false});
}

speakOnClick = () => {
    this.setState({ speakActive: true, fundraiseActive: false, donateActive: false});
}
Diogo Sgrillo
  • 2,601
  • 1
  • 18
  • 28
  • Note that this is part of the ES2016 spec and will likely require additional babel configuration: https://babeljs.io/docs/plugins/transform-class-properties/ That being said, I VASTLY prefer this method of `this` binding – taylorc93 Dec 22 '16 at 16:43
  • I like how clean this is – usednapkin Dec 22 '16 at 16:55