2016-11-27 53 views
0

我正在構建一個基於React的項目用於學習目的。我被困在製作一個表格組件,它呈現自己,然後向mbaas後端發送一個Ajax請求,以獲取所有書籍條目並將每個條目填充到新行中。這是我到目前爲止所做的。請原諒大塊的代碼,但因爲我還沒有完全理解的方法,狀態和渲染(之間的相互作用),這裏是整個類:React組件問題與狀態,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> 
     ); 
    } 
} 

現在BookRow是不是很有趣,只有onClick部分是相關的。它看起來像這樣:

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

如果登錄用戶不是本書的出版者,則鏈接不應該是可見的。 onClick調用來自BookTable的方法deleteBook(id)。在成功的ajax上,它應該從state.books(數組)和render中刪除這本書。

我對initBooks方法特別困惑。我在填充狀態的循環之前添加了日誌,並在狀態更新時作爲回調。日誌#1和日誌#3的結果相同,日誌#2#4的結果相同。另外,如果我擴展日誌#2(在setState之前)或日誌#4,那麼這兩個顯示狀態= [1]。這有什麼意義?此外,如果您查看日誌#1#3 - 他們打印[]。我可能錯過了一些內部組件交互,但我真的不知道是什麼。

謝謝。

+0

的setState動作是異步的,所以更改可能不會在this.state反映的時候了。 –

回答

1

setState不會立即更新狀態。所以在for循環的第二次迭代中,你不會得到新的狀態。因此,請先製作新的圖書清單,然後在新清單準備好後再進行設置。

試試這個:

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

感謝您的回答。 @自由的靈魂,也非常感謝。我會試一試。問題是,在我目前的測試中,循環只進行一次,因爲我只有一個測試記錄。在循環中設置狀態是愚蠢的,我應該想到這一點,但它仍然感到奇怪。讓我試試看,回到你身邊。 – Alex

+0

如果循環只進行一次,那麼它應該工作。我複製了你的代碼並試了一下。它對我很有用 – 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