質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

【React】TypeError: ** is not a function

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 676

southernX

score 5

Reactでのエラー

reactでTodoリストを作っています。入力した内容がリストで表示されます。

実現したい事としてはこの通りです。
inputタグ内で入力した内容がhandleChangeメソッドによりstateのinputに更新されます。

そして送信ボタンを押したら、handleSubmitメソッドによりstate.inputの内容がtoDoList(配列)に入れられます。

もしtoDoListに要素が一つでもあるなら、それをリストとしてレンダリングします。

起こった問題

しかし、送信ボタンを押すと二つの問題が発生しました。

まず、フォームに入力して「追加」ボタンを「一度」押すとリストが消えてしまうことです。

cccと入力してボタンを押してみました。

次にこの状態でもう一度ボタンを押すと次のエラーが発生しました。

以下がhandleSubmitメソッド内で起こったエラーです。

エラーメッセージ

TypeError: Todo.push is not a function

該当のソースコード

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// import App from './App';
import * as serviceWorker from './serviceWorker';

class Todo extends React.Component {
 constructor(props) {
   super(props);
   this.state = {
     input: '',
     toDoList: ["aaa", "bbb"],
   }
 }


 handleSubmit(e) {
  e.preventDefault();
  const input = this.state.input;
  const Todo = this.state.toDoList;
  this.setState({
    toDoList: Todo.push(input),
  });
 }


 handleChange(event) {
   const input = event.target.value;
   this.setState({
     input: input,
   });
 }

  render(){
    var toDoList;
    if (this.state.toDoList.length > 1) {
      toDoList = this.state.toDoList.map((list) => {
        return (
          <li>{list}</li>
        );
      });
    }

    return(
    <div className="container">
      <div className="Todo">Todoリスト</div>
      <ul>
        {toDoList}
      </ul>
      <form onSubmit={(e) => this.handleSubmit(e)}>
        <input type="text" onChange={(event) => this.handleChange(event)}/>
        <input type="submit" value="追加"/>
      </form>

    </div>
    )
  }
}

ReactDOM.render(
  <Todo />,
  document.getElementById('root')
);

試したこと

toDoListは配列であることを確かめるために試しに"aaa","bbb"という文字列を入れてみました。するとこれは正しく表示されました。renderメソッド内ではtoDoList.mapが動いているようです。

stateの定義の仕方が悪いのかと思った時はstate: new Arrayにしてみましたが解決しませんでした。

handleSubmit内でstate.toDoListを変数に代入する際、sliceメソッドで使ってみましたがすぐに的外れであることがわかりました。

this.state.toDoListは確かに配列で、pushメソッドも問題なく使えるはずなのですが・・・

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

こんにちは

以下は MDN の push メソッドの説明 からの引用です。

push() メソッドは、配列の末尾に 1 つ以上の要素を追加することができます。また戻り値として新しい配列の要素数を返します。

上記のとおり、 push メソッドは、要素が追加された後の配列の長さを返します。なので、ご質問のコードだと、1回目の「追加」ボタンをクリックしたのときの

this.setState({
    toDoList: Todo.push(input),
  });


によって、 this.state.toDoList には、1つ要素の追加された配列の長さが入るので、 3 という数値が設定されます。
そのため、 render の以下の部分

 if (this.state.toDoList.length > 1) {


で、 this.state.toDoList.length は undefined になり、 if が判定する条件は false になって、 <ul> の中身が作られないために、画面上では、既存のリストアイテムも消えてしまいます。 

さらに続けて「追加」ボタンをクリックしたときは、 

const Todo = this.state.toDoList;


によって const Todo には 3 という数値が入ってきて、

Todo.push(input)


を実行しようとしますが、3という数値にはpushというメソッドはないので、

TypeError: Todo.push is not a function


というエラーになります。
修正の方法ですが、新しい this.state.toDoList を setState する箇所の

修正前:

toDoList: Todo.push(input),


を、たとえば以下のように修正します。

修正後:

toDoList: [...Todo, input],


上記の一行の修正で、とりあえずは意図通り動くようになると思います。

さらに、ちょっとリファクタさせて頂けるならば、this.state.toDoList のほうも、同じ名前の toDoList という変数に入れるようにすれば、分割代入を使って以下のように書けます。

  handleSubmit(e) {
    e.preventDefault();
    const { input, toDoList } = this.state;
    this.setState({
      toDoList: [...toDoList, input],
    });
  }


あるいは、一時的な変数に入れないで、以下のようにも書けます。

  handleSubmit(e) {
    e.preventDefault();
    this.setState({
      toDoList: [
        ...this.state.toDoList,
        this.state.input
      ],
    });
  }

上記のように、 this.setState するときに、既存の state の何らかのプロパティの配列に追加したり、あるいはstateのあるプロパティはオブジェクトで、それのあるプロパティだけを変えたりするときに、スプレッド構文 ... はよく使われます。

以上参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/15 01:11

    解決しました!
    jsでのpushは数値を返すのですね。代わりにスプレッド構文で配列を更新することも初めて知りました。勉強になりました。
    スッキリしました。丁寧な回答ありがとうございます。

    キャンセル

  • 2019/08/15 01:13

    どういたしまして。 解決されたとのことで、よかったです 👏

    キャンセル

0

クラス名と変数名がかぶってるからじゃないですかね?


追記
これ

class Todo extends React.Component {


  const Todo = this.state.toDoList;


これ

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/15 01:29

    あぁ、ぱっと見てアレ?と思ったんですが、ハズレでしたか。
    まぁ一般的によろしくない事なので、名前は変えたほうがいいっすよ。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る