简体   繁体   中英

React & Typescript: Cannot read property '0' of undefined

I recently moved from .jsx to .tsx, and I'm having issues (before moving to typescript, was all working well). The first error is at line: " <Modal show={this.state.isOpen[key]} onClose={this.handleToggleModal.bind(__this,key)}> ". I suspect there's something related to typescript, but I don't know what. Anybody can help me?

// TaskCard.tsx

//Dependencies
import * as React from 'react';

//Copmponents
import Modal, {ModalProps} from './Modal';
import Input from './Input';
import {IBoard} from './Board';
import PostComment from './PostComment';

export interface IComments{
    body: string,
    from: string,
    date: string,
    hour: string
}

export interface ITask{
    board: string,
    duedate: string,
    tag: string,
    tagClass: string,
    tagText: string,
    body: string,
    risk: string,
    responsable: string
    comments: IComments[]
}

export interface TaskProps{
    tasks: ITask[]
    boards: IBoard[]
}

export interface TaskState{
    isOpen: {}
}

class TaskCard extends React.Component<TaskProps, TaskState>{
    constructor(props) {
        super(props);

        this.state = { 
            isOpen: props.isOpen
        };
    }

    handleToggleModal(key) {
        this.state.isOpen[key] = !this.state.isOpen[key];
        this.setState(this.state.isOpen);
    }

    render(){
        const { tasks, boards } = this.props;
        let __this = this;

        return(
            tasks && tasks.map(
                (tasks, key) =>
                <div key={key} className="v-margin no-margin-top card-contour medium" onClick={this.handleToggleModal.bind(__this,key)}>
                    <div className="row middle-xs caption">
                        <div className="col-xs-6 no-padding">
                            <div className="row middle-xs">
                                <img className="img_icn no-padding-left" src="./assets/img/icn_calendar.svg" alt=""/>
                                <p className="txt-tertiary-color">{tasks.duedate}</p>
                            </div>
                        </div>
                        <div className="col-xs-6 no-padding">
                            <div className="row end-xs middle-xs">
                                <div className={tasks.tagClass + " tag tag_box center-align"}>
                                    <p>{tasks.tag}</p>
                                    <span className="tag_hint">{tasks.tagText}</span>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div className="row middle-row v-padding">
                        <p>{tasks.body}</p>
                    </div>
                    <div className="row middle-xs caption">
                        <div className="col-xs-9 no-padding">
                            <div className="row middle-xs">
                                <img className="img_icn no-padding-left" src="./assets/img/icn_target.svg" alt=""/>
                                <p className="txt-tertiary-color">Riesgos: {tasks.risk}%</p>
                            </div>
                        </div>
                        <div className="col-xs-3 no-padding">
                            <div className="row middle-xs end-xs">
                                <img className="img_icn no-padding-left" src="./assets/img/icn_comments.svg" alt=""/>
                                <p className="txt-tertiary-color">{tasks.comments.length}</p>
                            </div>
                        </div>
                    </div>
                    <Modal show={this.state.isOpen[key]} onClose={this.handleToggleModal.bind(__this,key)}>
                        <div className="modal_container">
                            <section className="modal_section">
                                <div className="row middle-xs no-margin-left no-margin-right modal_section_title">
                                    <object width="20px" height="20px" data="./assets/img/icn_objetive-blue.svg"></object>
                                    <div className="col-xs start-xs">
                                        <h4 className="semi-bold">Objetivo</h4>
                                    </div>
                                </div>
                                <div className="col-xs-12 modal_section_content">
                                    <p>{tasks.body}</p>
                                </div>
                            </section>
                            <section className="modal_section">
                                <div className="row middle-xs no-margin-left no-margin-right modal_section_title">
                                    <object width="20px" height="20px" data="./assets/img/icn_target-blue.svg"></object>
                                    <div className="col-xs start-xs">
                                        <h4 className="semi-bold">Target</h4>
                                    </div>
                                </div>
                                <div className="col-xs-12 modal_section_content">
                                    <p>Riesgos / Problemas = {tasks.risk}%</p>
                                    <div className="table v-margin">
                                        <div className="row middle-xs">
                                            <div className="col-xs">
                                                <p className="table_head">Fecha de vencimiento</p>
                                                <p className="table_body">{tasks.duedate}</p>
                                            </div>
                                            <div className="col-xs">
                                                <p className="table_head">Tipo de objetivo</p>
                                                <p className="table_body">{tasks.tagText}</p>
                                            </div>
                                            <div className="col-xs">
                                                <p className="table_head">Responsable</p>
                                                <p className="table_body">{tasks.responsable}</p>
                                            </div>
                                            <div className="col-xs-12 v-margin no-margin-bottom">
                                                <p className="table_head">Estado de objetivo</p>
                                                <div className="row middle-xs">
                                                    {
                                                        boards && boards.map(
                                                        (boards, board) =>
                                                        <div key={board} className="col-xs-3">
                                                            <Input name="state" type="radio" classNameCustom="input_radio"
                                                            checked={boards.id == tasks.board} value={boards.id} id={boards.id}/>
                                                            <label htmlFor={boards.id}>{boards.name}</label>
                                                        </div>
                                                        )
                                                    }
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </section>
                            <section className="modal_section">
                                <div className="row middle-xs no-margin-left no-margin-right modal_section_title">
                                    <object width="20px" height="20px" data="./assets/img/icn_activity-blue.svg"></object>
                                    <div className="col-xs start-xs">
                                        <h4 className="semi-bold">Actividad</h4>
                                    </div>
                                </div>
                                <div className="col-xs-12 modal_section_content">
                                    {
                                        tasks.comments.length!=0 ?(
                                            tasks.comments.map((comments, i) => {
                                                return(
                                                    <div key={i} className="comment">
                                                        <div className="comment_box">
                                                            <p>{comments.body}</p>
                                                        </div>
                                                        <div className="comment_data row no-margin middle-xs">
                                                            <div className="col-xs start-xs">
                                                                <p>{comments.from}</p>
                                                            </div>
                                                            <div className="col-xs end-xs">
                                                                <p>{comments.date} a las {comments.hour} hs.</p>
                                                            </div>
                                                        </div>
                                                    </div>
                                                )
                                            })
                                        ):null                                
                                    }
                                </div>
                            </section>
                        </div>
                        <PostComment />
                    </Modal>
                </div>
            )
        )
    }
}

export default TaskCard;

And here is the Modal component

//Modal.tsx file
//Dependencies
import * as React from 'react';

export interface ModalProps{
    onClose: any,
    show: boolean,
    children: any
}

export default class Modal extends React.Component<ModalProps>{
    handleStopPropagation = (e) =>{
        e.stopPropagation();
    }

    render(){
        const {onClose, show, children} = this.props;

        // Render nothing if the "show" prop is false
        if(!this.props.show) {
            return null;
        }

        return (
            <div className="modal_bkg" onClick={this.handleStopPropagation}>
                <div className="modal_bkg_container row middle-xs center-xs">
                    <div className="modal card">
                        <div className="right-align modal_close">
                            <button className="btn btn_close" onClick={this.props.onClose}>x</button>
                        </div>
                        {this.props.children}
                    </div>
                </div>
            </div>
        );
    }
}

I'm unable to run the code at the moment, but as far as I can see the TaskCard.render function is using Array.map function, which is executing a callback function for every task item.

And there is a problem, because in the callback this is not the this you think it is :). According to documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map you can use map overload that accepts this .

I believe that if you do

return(tasks && tasks.map(
            (tasks, key) => <div>put your component markup here</div>,
            this)
      );

inside TaskCard.render function you should see the error no more.

A friend helped me with this and now it's working. The first problem was the state "isOpen" at start always is undefined so, that's why runtime was throwing error "Cannot read property '0' of undefined". So now, "isOpen" is and empty object and the final constructor looks like this:

constructor(props) {
    super(props);

    this.state = { 
        isOpen: {}
    };
}

After this, another error was "boards.map is not a function" and that was because I was passing the array "boards" as object. Changing for "board" was enought. So the final code was:

boards && boards.map(
    (board, key) =>
    <div key={key} className="col-xs-3">
       <Input name="state" type="radio" classNameCustom="input_radio"
       checked={board.id == task.board} value={board.id} id={board.id}/>
       <label htmlFor={board.id}>{board.name}</label>
    </div>
)

Anyway, thanks for trying to help me. Regards!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM