2017-04-10 65 views
0

我無法渲染具有來自狀態的數據的組件。我從Parse Server提取數據,然後使用該數據更新狀態。但是,在數據可用之前,我使用數據的組件(下面代碼中的<MenuItem />)正在渲染。使用Parse Server返回的數據更新狀態後,無法獲取React組件重新呈現

我想在這裏介紹的方法: How to wait for AJAX response and only after that render the component?

但我無法獲取組件識別正在更新的狀態,然後重新呈現。

任何想法?代碼全文如下。還附上了一張屏幕截圖,顯示成功的控制檯日誌和狀態數據的存在情況。

(應當指出的是,我是新來的反應,以便大量的代碼很可能不是最優的。)

import React from "react"; 
import Parse from 'parse'; 

import MenuItem from './MenuItem'; 

class MenuItems extends React.Component { 
    constructor() { 
    super(); 

    this.loadMenuItems = this.loadMenuItems.bind(this); 

    this.state = { 
     menuItems: {}, 
     order: {} 
    }; 
    } 

    loadMenuItems() { 
    /* 0a. Get copy of State? */ 
    const menuItems = {...this.state.menuItems}; 

    /* 0. Get User */ 
    // TODO: Get this from props 
    var user = Parse.User.current(); 

    /* 1. Get Menu Items for this user */ 
    var MenuItemTest = Parse.Object.extend('MenuItemTest'); 
    var query = new Parse.Query(MenuItemTest); 

    query.equalTo('user', user); 
    query.find({ 
     success: function(returnedMenuItems) { 
     console.log("Successfully retrieved " + returnedMenuItems.length + " menu items."); 

     for (var i = 0; i < returnedMenuItems.length; i++) { 
      var object = returnedMenuItems[i]; 

      const menuItem = { 
      name: object.get('name'), 
      price: object.get('price'), 
      description: object.get('description'), 
      } 

      menuItems[`menu-item-${object.id}`] = menuItem; 
     } 
     }, 
     error: function(error) { 
     console.log("Error: " + error.code + " " + error.message); 
     } 
    }); 

    this.setState({ menuItems }); 
    } 

    componentWillReceiveProps(nextProps) { 
    console.log(nextProps); 
    this.loadMenuItems(); 
    } 

    componentWillMount() { 
    this.loadMenuItems(); 
    } 

    render() { 
    // Example from: 
    // https://stackoverflow.com/questions/31723095/how-to-wait-for-ajax-response-and-only-after-that-render-the-component 

    if (!this.state.response) { 
     return <div>Loading...</div>; 
    } 

    if (this.state.response.length === 0) { 
     return <div>No menu items yet</div>; 
    } 

    return (
     <div className="menu-items"> 
     { 
      Object 
      .keys(this.state.menuItems) 
      .map(key => <MenuItem key={key} details={this.state.menuItems[key]} />) 
     } 
     </div> 
    ) 
    } 
} 

export default MenuItems; 

Screenshot showing state with items

Successful console log

更新的代碼一致與評論主題

import React from "react"; 
import Parse from 'parse'; 

import MenuItem from './MenuItem'; 

class MenuItems extends React.Component { 
    constructor() { 
    super(); 

    this.loadMenuItems = this.loadMenuItems.bind(this); 
    } 

    loadMenuItems() { 
    const menuItems = {}; 

    /* 0. Get User */ 
    var user = Parse.User.current(); 

    /* 1. Get Menu Items for this user */ 
    var MenuItemTest = Parse.Object.extend('MenuItemTest'); 
    var query = new Parse.Query(MenuItemTest); 

    query.equalTo('user', user); 
    query.find({ 
     success: function(returnedMenuItems) { 
     console.log("Successfully retrieved " + returnedMenuItems.length + " menu items."); 

     for (var i = 0; i < returnedMenuItems.length; i++) { 
      var object = returnedMenuItems[i]; 

      const menuItem = { 
      name: object.get('name'), 
      price: object.get('price'), 
      label: object.get('label'), 
      shorthand: object.get('shorthand'), 
      description: object.get('description') 
      } 

      menuItems[`menu-item-${object.id}`] = menuItem; 
     } 
     }, 
     error: function(error) { 
     console.log("Error: " + error.code + " " + error.message); 
     } 
    }); 

    this.setState({ menuItems }); 
    } 

    componentWillReceiveProps(nextProps) { 
    console.log('I ran'); 
    console.log(nextProps); 
    this.loadMenuItems(); 
    } 

    componentWillMount() { 
    this.loadMenuItems(); 
    } 

    render() { 
    // Handle case where the response is not here yet 
    if (!this.state.menuItems) { 
     console.log('No state'); 
     return <div>Loading...</div>; 
    } 

    // Gives you the opportunity to handle the case where the ajax request 
    // completed but the result array is empty 
    if (this.state.menuItems.length === 0) { 
     console.log('Zero items'); 
     return <div>No menu items yet</div>; 
    } 

    if (this.state.menuItems) { 
     return (
     <div className="menu-items"> 
      { console.log(this.state.menuItems) } 
      { console.log(Object.keys(this.state.menuItems)) } 
     </div> 
    ) 
    } 
    } 
} 

export default MenuItems; 

回答

0

我找到了解決方案。儘管在試圖解決我的問題時,我移動了一些代碼來設置父組件的狀態並將其作爲道具傳遞給MenuItems。但爲什麼它不工作仍然是一樣的。

它與loadMenuItems()做:

loadMenuItems() { 
    const menuItems = {}; 

    /* 0. Get User */ 
    var user = Parse.User.current(); 

    /* 1. Get Menu Items for this user */ 
    var MenuItemTest = Parse.Object.extend('MenuItemTest'); 
    var query = new Parse.Query(MenuItemTest); 

    query.equalTo('user', user); 
    query.find({ 
    success: function(returnedMenuItems) { 
     console.log("Successfully retrieved " + returnedMenuItems.length + " menu items."); 

     for (var i = 0; i < returnedMenuItems.length; i++) { 
     var object = returnedMenuItems[i]; 

     const menuItem = { 
      name: object.get('name'), 
      price: object.get('price'), 
      label: object.get('label'), 
      shorthand: object.get('shorthand'), 
      description: object.get('description') 
     } 

     menuItems[`menu-item-${object.id}`] = menuItem; 
     } 
    }, 
    error: function(error) { 
     console.log("Error: " + error.code + " " + error.message); 
    } 
    }); 

    this.setState({ menuItems }); 
} 

發生了什麼事是在執行success回調之前this.setState({ menuItems })正在運行。所以狀態總是空的,但是當我檢查控制檯中的所有內容時,它已經返回,並通過menuItems[菜單項 - $ {object.id} ] = menuItem;並且一切都在那裏。

所以我所做的就是在success回調移動this.setState({ menuItems });,然後添加.bind(this)這樣我就可以設置狀態有:

success: function(returnedMenuItems) { 
    ... 
}.bind(this), 

,我相信部件從未更新,因爲雖然狀態是與menuItems[菜單設置-item - $ {object.id} ] = menuItem;循環this.setState()從未再次被調用,這是觸發組件刷新的原因。我可能會忽略這一點,但我認爲這是發生了什麼。

我已在下面發佈更新的代碼以備將來參考。

父組件

import React from "react"; 
import Parse from 'parse'; 

import MenuItems from './MenuItems'; 


class ManageMenu extends React.Component { 
    constructor() { 
    super(); 

    this.loadMenuItems = this.loadMenuItems.bind(this); 
    } 

    loadMenuItems() { 
    const menuItems = {}; 

    /* 0. Get User */ 
    var user = Parse.User.current(); 

    /* 1. Get Menu Items for this user */ 
    var MenuItemTest = Parse.Object.extend('MenuItemTest'); 
    var query = new Parse.Query(MenuItemTest); 

    query.equalTo('user', user); 
    query.find({ 
     success: function(returnedMenuItems) { 
     console.log("Successfully retrieved " + returnedMenuItems.length + " menu items."); 

     for (var i = 0; i < returnedMenuItems.length; i++) { 
      var object = returnedMenuItems[i]; 

      const menuItem = { 
      name: object.get('name'), 
      price: object.get('price'), 
      label: object.get('label'), 
      shorthand: object.get('shorthand'), 
      description: object.get('description') 
      } 

      menuItems[`menu-item-${object.id}`] = menuItem; 
     } 

     this.setState({ menuItems }); 
     }.bind(this), 
     error: function(error) { 
     console.log("Error: " + error.code + " " + error.message); 
     } 
    }); 
    } 

    componentWillMount() { 
    this.loadMenuItems(); 
    } 

    render() { 
    // Handle case where the response is not here yet 
    // console.log(this.state); 
    if (!this.state) { 
     console.log('No state'); 
     // Note that you can return false it you want nothing to be put in the dom 
     // This is also your chance to render a spinner or something... 
     return <div>Loading...</div>; 
    } 

    if (this.state.menuItems) { 
     return (
     <div className="manage-menu"> 
      <div className="mt3 border-top"> 
      <h3 className="mt1 bold s2">Menu Items</h3> 

      <div className="mt1"> 
       <MenuItems menuItems={this.state.menuItems} /> 
      </div> 
      </div> 
     </div> 
    ) 
    } 
    } 
} 

export default ManageMenu; 

的MenuItems組件

import React from "react"; 

import MenuItem from './MenuItem'; 

class MenuItems extends React.Component { 
    render() { 
    // Same as: const menuItems = this.props.menuItems; 
    const { menuItems } = this.props; 

    // No menu items yet 
    if (menuItems.length === 0) { 
     console.log('Zero items'); 
     return <div>No menu items yet</div>; 
    } 

    if (menuItems) { 
     return (
     <div className="menu-items"> 
      { 
      Object 
       .keys(menuItems) 
       .map(key => <MenuItem key={key} details={menuItems[key]} />) 
      } 
     </div> 
    ) 
    } 
    } 
} 

export default MenuItems; 
1

至於什麼我可以從你得到了什麼在這裏說,

if (!this.state.response) { 
    return <div>Loading...</div>; 
} 

if (this.state.response.length === 0) { 
    return <div>No menu items yet</div>; 
} 

this.state.response狀態從未設置,因此將始終顯示Loading...

我相信你想將其更改爲

if (!this.state.menuItems) { 
    return <div>Loading...</div>; 
} 

if (this.state.menuItems.length === 0) { 
    return <div>No menu items yet</div>; 
} 

和您的constructor t o根本沒有設置menuItems狀態(所以裝載最初顯示)。

+0

這固定的「載入中...」掛起,謝謝!現在我遇到另一個問題,我認爲這與循環狀態對象有關。如果我在'return(

)'函數中'console.log(this.state.menuItems)',我可以看到狀態在那裏。但是,'Object'映射似乎沒有循環。如果我簡單地返回''我可以在''的單個實例上看到所有狀態對象作爲道具。當然,我想爲每個處於狀態的對象提供一個''實例。任何明顯的我做錯了? –

+0

代碼中的問題是: '回報(

{ Object.keys(this.state.menuItems).map(key =>) }
)' –

+0

沒有什麼明顯的,我可以看到...如果你只是do'this.state.menuItems.map(項目=><菜單項關鍵= {} item.somethingUnique細節= {item} />)'是否按預期工作?或者在地圖內記錄「鑰匙」? (關鍵字=> {console.log(key); return })''Object.keys(this.state.menuItems) –

相關問題