2016-11-27 5 views
0

私は研究目的でReactベースのプロジェクトを構築しています。私は自分自身をレンダリングし、mbaasバックエンドにすべてのブックエントリーを取得し、それぞれを新しい行に塗りつぶすためにajaxリクエストを送信するテーブルコンポーネントの作成に固執しています。ここまで私がこれまでに出てきたのはこれです。コードの大きな塊を許すが、私はまだ完全には)メソッド、状態(レンダリングの間の相互作用を理解していないため、ここでは、クラス全体でください:今BookRowはなく、非常に興味深いです状態、setState、レンダリングでコンポーネントに問題が発生する

class BooksTable extends Component { 
    constructor(props) { 
     super(props); 
     this.state = { 
      books: [] 
     }; 

     this.updateState = this.updateState.bind(this); 
     this.initBooks = this.initBooks.bind(this); 
    } 

    componentDidMount() { 
     let method = `GET`; 
     let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}`; 
     let headers = { 
      "Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`, 
      "Content-Type": `application/json` 
     }; 

     let request = {method, url, headers}; 
     $.ajax(request) 
      .then(this.initBooks) 
      .catch(() => renderError(`Unable to connect. Try again later.`)); 
    } 

    deleteBook(id) { 
     let method = `DELETE`; 
     let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}/${id}`; 
     let headers = { 
      "Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`, 
      "Content-Type": `application/json` 
     }; 

     let request = {method, url, headers}; 
     $.ajax(request) 
      .then(() => this.updateState(id)) 
      .catch(() => renderError(`Unable to delete, something went wrong.`)); 
    } 

    updateState(id) { 
     for (let entry of this.state.books.length) { 
      if (entry.id === id) { 
       // Pretty sure this will not work, but that is what I've figured out so far. 
       this.state.books.splice(entry); 
      } 
     } 
    } 

    initBooks(response) { 
     console.log(`#1:${this.state.books}); 
     console.log(`#2:${this}); 
     for (let entry of response) { 
      this.setState({ 
       books: this.state.books.concat([{ 
        id: entry._id, 
        name: entry.name, 
        author: entry.author, 
        description: entry.description, 
        price: Number(entry.name), 
        publisher: entry.publisher 
       }]) 
      },() => { 
       console.log(`#3${this.state.books}`); 
       console.log(`#4${this}`); 
      }); 
     } 
    } 

    render() { 
     return (
      <div id="content"> 
       <h2>Books</h2> 
       <table id="books-list"> 
        <tbody> 
         <tr> 
          <th>Title</th> 
          <th>Author</th> 
          <th>Description</th> 
          <th>Actions</th> 
         </tr> 
         {this.state.books.map(x => 
          <BookRow 
           key={x.id} 
           name={x.name} 
           author={x.author} 
           description={x.description} 
           price={x.price} 
           publisher={x.publisher} />)} 
        </tbody> 
       </table> 
      </div> 
     ); 
    } 
} 

のみonClick部分は関連しています。

<a href="#" onClick={() => this.deleteBook(this.props.id)}>{owner? `[Delete]` : ``}</a> 

ログインしたユーザーが本の出版社でない場合、リンクは表示されません。 onClickはBookTableのメソッドであるdeleteBook(id)を呼び出します。成功したajaxでstate.books(配列)から本を削除してレンダリングする必要があります。

私は特にinitBooksメソッドについて混乱しています。ステートを更新するループの前にログを追加し、ステートが更新されたときのコールバックとしてログを追加しました。ログ#1とログ#3の結果は同じであり、ログ#2#4も同じです。また、ログ#2(setStateの前)またはログ#4を展開すると、両方とも状態= [1]と表示されます。これはどういう意味ですか?さらに、ログ#1#3を見ると、[]が印刷されます。おそらく内部コンポーネントのやりとりが欠けているかもしれませんが、実際には何が分かりませんか?

ありがとうございました。

+0

setStateアクションは非同期なので、変更はthis.stateにすぐに反映されない可能性があります。 –

答えて

1

setStateはすぐに状態を更新しません。だからあなたのforループの2番目の反復では、新しい状態を得ることはありません。新しい書籍リストを作成してから、新しいリストが作成されたら設定してください。

これを試してみてください:

initBooks(response) { 
    console.log(this.state.books, "new books not set yet") 
    let newBooks = [] 
    for (let entry of response) { 
    newBooks.push({ 
       id: entry._id, 
       name: entry.name, 
       author: entry.author, 
       description: entry.description, 
       price: Number(entry.name), 
       publisher: entry.publisher 
      }) 
    } 
    this.setState({books: [...this.state.books, newBooks]},() => { 
     console.log(this.state.books, "new books set in the state") 
    }) 
} 
+0

お返事ありがとうございます。 @ free-soul、ありがとう。私はそれを試してみましょう。私の現在のテストでは、テストレコードが1つしかないので、ループは1回だけになります。ループの状態を設定することは愚かです、私はそれを考えたはずですが、それでも奇妙な感じです。私はそれを試してみましょう。 – Alex

+0

ループが1回だけ実行される場合は、ループが機能するはずです。あなたのコードをコピーして試しました。それは私のために働いた – Swapnil

+0

私は思ったように - あなたのポイントは、ループのsetStateを移動する良いですが、それは私の問題に役立つことはありません。あなたのコードを実行する - それは '新しい州で設定された本'を印刷しません。私は重要な財産について警告を受けるだけです。ログ#1#2が同じオブジェクトをログに記録しているため、異なる結果がどのように出力されるのかまだ説明できません。印刷されたオブジェクトが、長さ1の更新された状態を示し、サーバー応答から受け取ったオブジェクトを含むのはなぜですか。私は一時的な配列の所有者をまだ初期化していません。 – Alex

1

はこれを試してみてください。私はここにいた何

initBooks(response = {}) { 
    const books = Object.keys(response); 

    if (books.length > 0) { 
     this.setState((prevState) => { 
      const newBooks = books.reduce((acc, key) => { 
       const entry = response[key]; 

       return [ ...acc, { 
        id: entry._id, 
        name: entry.name, 
        author: entry.author, 
        description: entry.description, 
        price: Number(entry.name), 
        publisher: entry.publisher 
       }]; 
      }, prevState.books); 

      return { books: newBooks }; 
     }); 
    } 
} 

?必要な場合にのみ

  • setState(すなわちAPI応答からデータがある場合のみ)
  • にはsetState
  • ループ内信頼性をアトミック 更新を確実にするために、関数( (prevState, props) => newState)を使用しない、状態変異を回避します。

ポイントは熟考する:

  • ループ内setStateを避け、あなたの状態を変異させないでください。代わりに新しい状態オブジェクトを準備し、一回setStateを行う)

  • あなたがsetStateの呼び出し中に以前の状態にアクセスする場合、それはこのようにそれを行うにはadvised次のとおりです。

    this.setState((prevState, props) => { 
        return { counter: prevState.counter + props.increment }; 
    }); 
    

    setState()即座にthis.stateに変更されませんが、 保留中の状態遷移が作成されます。この メソッドを呼び出した後にthis.stateにアクセスすると、既存の値が返される可能性があります。

+0

素敵なヒントの要約。ありがとう。 – Alex

関連する問題