Ben
Ben

Reputation: 13

ReactJS Component function call this and props undefined

this may be a simple React question, but I am a new starter. Having done some tutorials and excellent online courses, I am attempting my first proper ReactJS project. I've also bitten of ES6 and JSX, so that may be the issue. Basically, using an array passed in as props and then calling sub functions to carry out login on the data structure I want to pass in the function to be called onClick as the props, but "this" is undefined in the functions, though it is being passed into the array.map functions...

import React from 'react';
import { Link, Location } from 'react-router';

import moment from 'moment';
import _ from 'lodash';

import IBoxTools from '../common/iboxTools';

class Timesheets extends React.Component {

    componentDidMount() {
        $('.timesheets li button').tooltip();
    }

    render() {

        const showTimesheets = function (timesheets) {

            if (timesheets && timesheets.length) {
                return (
                    <div>
                        <ul className="list-group timesheets">
                            {timesheets.map(timesheetCandidateRow, this)}
                        </ul>
                        <Link to="/timesheets" className="btn btn-primary">View all timesheets</Link>
                    </div>
                );
            }
            else {
                return (
                    <div>
                        <p>There are currently no timesheets requiring action</p>
                        <a className="btn btn-primary" href="#">View all timesheets</a>
                    </div>);
            }
        }

        const timesheetCandidateRow = function (timesheetCandidate) {
            var candidateName = timesheetCandidate.candidate.forename + ' ' + timesheetCandidate.candidate.surname;
            return (
                <li className="list-group-item" key={timesheetCandidate.id}>
                    <div>
                        <h4 className="list-group-item-heading">{candidateName}<br /><small>{timesheetCandidate.candidate.title}</small></h4>
                        <table className="table table-condensed">
                            <tbody>
                                {timesheetCandidate.timesheets.map(timesheetRow, this)}
                            </tbody>
                        </table>
                    </div>
                </li>
            );
        }

        const timesheetRow = function (timesheet) {
            var timesheetDate = moment(timesheet.date).format('dddd Do MMM');
            if (timesheet.startTime) {
                timesheetDate = timesheetDate + ' ' + timesheet.startTime + ' to ' + timesheet.endTime;
            }
            return (
                <tr key={timesheet.id}>
                    <td>{timesheetDate}</td>
                    <td>{timesheet.charge}</td>
                    <td style={{ width: 70 }}>
                        <div className="btn-group">
                            <button className="btn btn-success btn-xs" onClick={this.props.onAccept} title="Accept" data-toggle="tooltip" data-placement="bottom"><i className="fa fa-check"></i></button>
                            <button className="btn btn-danger btn-xs" onClick={this.props.onReject} title="Reject" data-toggle="tooltip" data-placement="bottom"><i className="fa fa-close"></i></button>
                        </div>
                    </td>
                </tr>
            );
        }

        return (
            <div className="ibox float-e-margins">
                <div className="ibox-title">
                    <h5>
                        <span className="fa-stack">
                            <i className="fa fa-square-o fa-stack-2x"></i>
                            <i className="fa fa-check fa-stack-1x"></i>
                        </span> Timesheets
                                </h5>
                    <IBoxTools />
                </div>
                <div className="ibox-content">
                    {showTimesheets(this.props.timesheets)}
                </div>
            </div>
        );
    }

}

Timesheets.propTypes = {
    timesheets: React.PropTypes.array,
    onAccept: React.PropTypes.func.isRequired,
    onReject: React.PropTypes.func.isRequired
}

export default Timesheets;

So, within the consts within the render method, "this" is undefined. Any help please?

Thanks.

Upvotes: 1

Views: 1060

Answers (2)

Ruan Mendes
Ruan Mendes

Reputation: 92274

To "lock down" this, you can use an arrow function

 const showTimesheets =  (timesheets) => {...}

Same for timesheetCandidateRow and any other inner functions.

You could use the bind solution posted by Vlad also, but that requires that you make sure your functions are called passing the correct this(using bind, call, or apply)

Upvotes: 3

Vlad Tamas
Vlad Tamas

Reputation: 180

Instead of passing this as a normal param, try binding your function to the correct calling object in all 3 places where you context switch:

timesheets.map(timesheetCandidateRow.bind(this))

timesheetCandidate.timesheets.map(timesheetRow.bind(this))

showTimesheets.bind(this)(this.props.timesheets)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Upvotes: 0

Related Questions