1

My app has 2 components.When I pass props from parent to child then i iterate the props using map and return a list in todoItemListjs component but it gives an error

A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.

Here are my components

TodoList

    var React=require('react');
import TodoListItem from './TodoListItem.js';
    var TodoList = React.createClass({
        Remove: function(e){
           this.props.onDelete(e);
        },
        DragStart: function(e){
            this.dragged = e.currentTarget;
            e.dataTransfer.effectAllowed = 'move';
        },
        DragEnd: function(e){
            this.dragged.style.display="";
            var IshasNode = false

            Array.prototype.forEach.call (this.dragged.parentNode.childNodes, function (node) {
                if(node.className=="placeholder")
                                IshasNode = true;

            } );
            if(!IshasNode)
            return;
            this.dragged.parentNode.removeChild(placeholder);
            var data = this.props.items;
            var from = Number(this.dragged.dataset.id);
            var to = Number(this.over.dataset.id);
            if(from < to) to--;
            if(this.nodePlacement == "after") to++;
            data.splice(to, 0, data.splice(from, 1)[0]);
            this.setState({data: data});    
        },
        DragOver: function(e) {

            e.preventDefault();
            this.dragged.style.display = "none";

            if(e.target.className == "placeholder") return;
            this.over = e.target;

            var relY = e.clientY - this.over.offsetTop;
            var height = this.over.offsetHeight / 2;
            var parent = e.target.parentNode;

            if(relY > height) {
              this.nodePlacement = "after";
              parent.insertBefore(placeholder, e.target.nextElementSibling);
            }
            else if(relY < height) {
              this.nodePlacement = "before"
              parent.insertBefore(placeholder, e.target);
            }
        },
        render: function() {

            var createItem = function(itemText,i) {


                return (

                    <TodoListItem key={i} value={i} onDragEnd={this.DragEnd}
            onDragStart={this.DragStart} onRemove = {this.Remove} AllItems={itemText}>{itemText}</TodoListItem>


                );
            };
            var allitems = this.props.items;
            // Here is the filter function 
            var status = this.props.filter[0].Status;
            switch (status){
                case 'false':
                 allitems = allitems.filter(t => !t.isDone)
                 break;
                 case 'true':
                 allitems = allitems.filter(t => t.isDone)
            };
            // Here is the search function 
            var queryText = this.props.filter[0].keyword;

            if(queryText){
                var queryResult=[];
                allitems.forEach(function(item){
                    if(item.item.toLowerCase().indexOf(queryText)!=-1)
                    queryResult.push(item);
                });
                return <ul onDragOver={this.DragOver}>{queryResult.map(createItem,this)}</ul>;
            }

            return  <ul onDragOver={this.DragOver}>{allitems.map(createItem,this)}</ul> ;
        }
    });
export default TodoList;

TodoListItem

    var React=require('react');


var TodoListItem = React.createClass({
getInitialState : function(){
            return {items:[{item:'',isDone:false,cost:0}]};
        },


    componentDidMount: function() {

  },

        ChangeHandler: function(e){
            this.setState({
              value: e.target.checked
            });
            this.props.children.isDone = e.target.checked;
        },
        RemoveHandler: function(){
           this.props.onRemove(this.props.value);
        },
        DragEndHandler : function(e){
                this.props.onDragEnd(e);
            },
        DragStartHandler : function(e){
                this.props.onDragStart(e);
        },
        render: function(){




            var _style = "line-through";
            var space="                                         ";
            if(!this.props.children.isDone)
            _style ="none";

    this.props.AllItems.items.map(function(todo)
        {

        console.log(todo.cost)
        return (

              <li data-id={this.props.value} 
                        key={this.props.value} draggable="true" onDragEnd={this.DragEndHandler}
                    onDragStart={this.DragStartHandler}><button type="button" className="close pull-right" aria-hidden="true" onClick={this.RemoveHandler}>&times;</button><input type="checkbox" onChange={this.ChangeHandler} defaultChecked={this.props.children.isDone} /><span style={{"textDecoration": _style}}>{todo.item}{todo.cost}</span></li>
            );
        },this)


        }
    });

export default TodoListItem;

I tried to wrap the code inside div tag also but it did not work

Pease guide

1

2 Answers 2

3

Because you are not returning anything from TodoListItem component, check the render method.

Either put the map part inside a div like this:

return(
    <div>
        {
            this.props.AllItems.items.map(function(todo){
                ....
            }
        }
    </div>
)

Or store the map result in a variable and return the final result, like this:

render(){
    let data = this.props.AllItems.items.map(function(todo){
        ....
    }

    return(
        <div> {data} </div>
    );
}

As suggested by @ivarni in comment: The <li> elements are supposed to go inside a <ul> the div is not allowed there, so change the structure according to that i just provided a reason of the error.

Reference.

Sign up to request clarification or add additional context in comments.

4 Comments

Assuming the <li> elements are supposed to go inside a <ul> the div is not allowed there. See details here. You might consider not recommending they break the DOM spec :)
@ivarni thanks for pointing out that, but code was TL;DR so just checked the issue with map part, and gave the idea what's the issue, will edit the answer :)
Yeah, it was pretty painful to read it. The formatting didn't help either :)
yes i was also going to point out that.Thanks @ivarni and mayank for your help:)
1

You return nothing in your TodoListItem render function. Your return is from the map callback. Wrap everything in a div and return it.

return (<div>
   {this.props.AllItems.items.map(function(todo)
    {

    console.log(todo.cost)
    return (

          <li data-id={this.props.value} 
                    key={this.props.value} draggable="true" onDragEnd={this.DragEndHandler}
                onDragStart={this.DragStartHandler}><button type="button" className="close pull-right" aria-hidden="true" onClick={this.RemoveHandler}>&times;</button><input type="checkbox" onChange={this.ChangeHandler} defaultChecked={this.props.children.isDone} /><span style={{"textDecoration": _style}}>{todo.item}{todo.cost}</span></li>
        );
    },this);}
</div>)

1 Comment

Assuming the <li> elements are supposed to go inside a <ul> the div is not allowed there. See details here. You might consider not recommending they break the DOM spec :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.