2016-06-01 18 views
9

Tôi khá mới đối với React JS và tôi có giao diện người dùng đơn giản này mà tôi cần để tạo. Về cơ bản tôi có một danh sách các loại và nếu tôi nhấp vào một danh mục, một danh sách các mục sẽ hiển thị trong danh mục đó. Nó sẽ ẩn danh sách các mục nếu tôi nhấp vào một danh mục khác.Cách hiển thị danh sách các mục khác nhau khi nhấp vào một danh mục

Tôi đã được cung cấp hai API, một API chứa JSON của các danh mục và một chứa các mục.

Tôi đã quản lý để tìm nạp dữ liệu từ API và nhổ chúng ra trên DOM. Tuy nhiên, tôi thấy khó để ghép thành phần lại với nhau để chỉ hiển thị các mục phù hợp khi danh mục của nó đã được nhấp.

Tôi đang sử dụng Babel để chuyển cú pháp JSX của tôi và sử dụng các trục để tìm nạp Dữ liệu. Tại thời điểm trang của tôi chỉ phun ra tất cả các mục và tất cả các loại. Hiểu được nhà nước rất khó cho tôi.

Bất kỳ lời khuyên nào cho người mới sử dụng Reactjs leaner? Cảm ơn!

Hai API của tôi có thể được tìm thấy trong mã của tôi vì tôi không có đủ điểm đại diện để đăng liên kết.

My JSX:

var React = require('react'); 
var ReactDOM = require('react-dom'); 
var axios = require('axios'); 



var NavContainer = React.createClass({ 

    getInitialState: function() { 
    return { 
     category: [], 
     items: [] 
    } 
    }, 

    // WHAT IS CURRENTLY SELECTED 
    handleChange(e){ 
     this.setState({data: e.target.firstChild.data}); 
    }, 

    componentDidMount: function() { 
    // FETCHES DATA FROM APIS 
    var th = this; 
    this.serverRequest = 
     axios.all([ 
     axios.get('https://api.gousto.co.uk/products/v2.0/categories'), 
     axios.get('https://api.gousto.co.uk/products/v2.0/products?includes[]=categories&includes[]=attributes&sort=position&image_sizes[]=365&image_sizes[]=400&period_id=120') 
     ]) 
     .then(axios.spread(function (categoriesResponse, itemsResponse) { 
     //... but this callback will be executed only when both requests are complete. 
     console.log('Categories', categoriesResponse.data.data); 
     console.log('Item', itemsResponse.data.data); 
     th.setState({ 
      category: categoriesResponse.data.data, 
      items : itemsResponse.data.data, 
      }); 
     })); 


    }, 

    componentWillUnmount: function() { 
    this.serverRequest.abort(); 
    }, 

    render: function() { 
    return (

     <div className="navigation"> 
      <h1>Store Cupboard</h1> 
      <NavigationCategoryList data={this.state.category} handleChange={this.handleChange}/> 
      <NavigationSubCategoryList data={this.state.category} subData={this.state.items} selected_category={this.state.data} /> 
     </div> 
    ) 
    } 
}); 

var NavigationCategoryList = React.createClass({ 
    render: function() { 
      var handleChange = this.props.handleChange; 

     // LOOPS THE CATEGORIES AND OUTPUTS IT 
     var links = this.props.data.map(function(category) { 
      return (
       <NavigationCategory title={category.title} link={category.id} handleChange={handleChange}/> 
      ); 
     }); 
     return (
      <div> 
       <div className="navigationCategory"> 
        {links} 
       </div> 
      </div> 
     ); 
    } 
}); 

var NavigationSubCategoryList = React.createClass({ 
    render: function() { 
      var selected = this.props.selected_category; 
     var sub = this.props.subData.map(function(subcategory) { 
      if(subcategory.categories.title === selected) 
      return (
       <NavigationSubCategoryLinks name={subcategory.title} link={subcategory.link} /> 
      ); 
     });      
     return (
      <div className="subCategoryContainer"> 
       {sub} 
      </div> 
     ); 
    } 
}); 

var NavigationSubCategoryLinks = React.createClass({ 
    render: function() { 
     return (
      <div className="navigationSubCategory" id={this.props.name}> 
      {this.props.name} 
      </div> 
     ); 
    } 
}); 



var NavigationCategory = React.createClass({ 
    render: function() { 
      var handleChange = this.props.handleChange; 
     return (
      <div className="navigationLink"> 
       <a href={this.props.link} onClick={handleChange}>{this.props.title}</a> 
      </div> 
     ); 
    } 
}); 



ReactDOM.render(<NavContainer />, document.getElementById("app")); 

Đây là một ảnh chụp màn hình của những gì tôi có trên trang web của tôi cho đến nay. Tất cả mọi thứ chỉ đổ trên màn hình. Các liên kết màu xanh lam là các danh mục.

Screenshot of current web page

+1

Bạn đã bao giờ tìm ra? – montrealist

+0

Tôi sẽ cố gắng viết ra một câu trả lời cụ thể cho câu hỏi của bạn sau này nhưng hãy kiểm tra jsbin này thể hiện một cái gì đó tương tự như những gì bạn đang cố gắng làm. Ghi chú 'items.filter (...)' vào giữa chừng. Đây là chìa khóa để lọc qua một tập hợp các kết quả và chỉ chọn ra các kết quả cụ thể. Bạn sẽ cần phải viết một hàm kiểm tra 'id' của mỗi mục và so sánh nó với' selectedCategoryId' và chỉ chọn ra những hàm phù hợp. http://jsbin.com/yotucu/1/embed?html,js,output – jaybee

+0

Cảm ơn bạn @ dannyid; bất kỳ suy nghĩ nào về cách thực hành tốt nhất Cách phản ứng để tổ chức mã dựa trên tập hợp các loại và mục hiện có (mối quan hệ nhiều-nhiều) đến từ máy chủ? – montrealist

Trả lời

7

Tôi tin rằng tôi có phiên bản dành cho bạn. Tôi đã thay đổi một số cú pháp và tên biến/prop cho rõ ràng và thêm ý kiến ​​giải thích các thay đổi.

const React = require('react'); 
const ReactDOM = require('react-dom'); 
const axios = require('axios'); 

// These should probably be imported from a constants.js file 
const CATEGORIES_ENDPOINT = 'https://api.gousto.co.uk/products/v2.0/categories'; 
const PRODUCTS_ENDPOINT = 'https://api.gousto.co.uk/products/v2.0/products?includes[]=categories&includes[]=attributes&sort=position&image_sizes[]=365&image_sizes[]=400&period_id=120'; 

const NavContainer = React.createClass({ 
    // All your state lives in your topmost container and is 
    // passed down to any component that needs it 
    getInitialState() { 
    return { 
     categories: [], 
     items: [], 
     selectedCategoryId: null 
    } 
    }, 

    // Generic method that's used to set a selectedCategoryId 
    // Can now be passed into any component that needs to select a category 
    // without needing to worry about dealing with events and whatnot 
    selectCategory(category) { 
    this.setState({ 
     selectedCategoryId: category 
    }); 
    }, 

    componentDidMount() { 
    this.serverRequest = axios.all([ 
     axios.get(CATEGORIES_ENDPOINT), 
     axios.get(PRODUCTS_ENDPOINT) 
    ]) 
    .then(axios.spread((categoriesResponse, itemsResponse) => { 
     console.log('Categories', categoriesResponse.data.data); 
     console.log('Item', itemsResponse.data.data); 

     // This `this` should work due to ES6 arrow functions 
     this.setState({ 
     categories: categoriesResponse.data.data, 
     items : itemsResponse.data.data 
     }); 
    })); 
    }, 

    componentWillUnmount() { 
    this.serverRequest.abort(); 
    }, 

    render() { 
    // ABD: Always Be Destructuring 
    const { 
     categories, 
     items, 
     selectedCategoryId 
    } = this.state; 

    return (
     <div className="navigation"> 
     <h1> 
      Store Cupboard 
     </h1> 

     <NavigationCategoryList 
      categories={categories} 
      // Pass the select function into the category list 
      // so the category items can call it when clicked 
      selectCategory={this.selectCategory} /> 

     <NavigationSubCategoryList 
      items={items} 
      // Pass the selected category into the list of items 
      // to be used for filtering the list 
      selectedCategoryId={selectedCategoryId} /> 
     </div> 
    ); 
    } 
}); 

const NavigationCategory = React.createClass({ 
    // Prevent natural browser navigation and 
    // run `selectCategory` passed down from parent 
    // with the id passed down from props 
    // No querying DOM for info! when props have the info we need 
    handleClick(e) { 
    const { id, selectCategory } = this.props; 
    // Handle the event here instead of all the way at the top 
    // You might want to do other things as a result of the click 
    // Like maybe: 
    // Logger.logEvent('Selected category', id); 
    e.preventDefault(); 
    selectCategory(id); 
    }, 

    render() { 
    const { id, title } = this.props; 
    return (
     <div className="navigationLink"> 
     <a href={id} onClick={this.handleClick}> 
      {title} 
     </a> 
     </div> 
    ); 
    } 
}); 
const NavigationCategoryList = React.createClass({ 
    // If you put your mapping method out here, it'll only 
    // get instantiated once when the component mounts 
    // rather than being redefined every time there's a rerender 
    renderCategories() { 
    const { selectCategory, categories } = this.props; 

    return categories.map(category => { 
     const { id, title } = category; 
     return (
     <NavigationCategory 
      // Every time you have a list you need a key prop 
      key={id} 
      title={title} 
      id={id} 
      selectCategory={selectCategory} /> 
    ); 
    }); 
    }, 

    render() { 
    return (
     <div> 
     <div className="navigationCategory"> 
      {this.renderCategories()} 
     </div> 
     </div> 
    ); 
    } 
}); 

const NavigationSubCategoryLink = React.createClass({ 
    render() { 
    const { name } = this.props; 
    return (
     <div className="navigationSubCategory" id={name}> 
     {name} 
     </div> 
    ); 
    } 
}); 
const NavigationSubCategoryList = React.createClass({ 
    renderSubCategories() { 
    const { selectedCategoryId, items } = this.props; 
    // This is the key to filtering based on selectedCategoryId 
    return items.filter(item => { 
     // Checking all the categories in the item's categories array 
     // against the selectedCategoryId passed in from props 
     return item.categories.some(category => { 
     return category.id === selectedCategoryId; 
     }); 
    }) 
    // After filtering what you need, map through 
    // the new, shorter array and render each item 
    .map(item => { 
     const { title, link, id } = item; 
     return (
     <NavigationSubCategoryLink 
      key={id} 
      name={title} 
      link={link} /> 
    ); 
    }); 
    }, 

    render() { 
    return (
     <div className="subCategoryContainer"> 
     {this.renderSubCategories()} 
     </div> 
    ); 
    } 
}); 

ReactDOM.render(<NavContainer />, document.getElementById('app')); 

Hai phần quan trọng cho việc lọc ở đây là những .filter().some() phương pháp trên mảng.

return items.filter(item => { 
    return item.categories.some(category => { 
    return category.id === selectedCategoryId; 
    }); 
}) 

Điều này có nghĩa là: Lặp lại tất cả các số items. Đối với mỗi item, hãy lặp lại qua số categories và kiểm tra xem có bất kỳ số nào trong số id s của chúng giống với selectedCategoryId hay không. Nếu một trong số đó là, tuyên bố .some() sẽ trả lại true gây ra điều đó item trong số .filter() để trả lại true, khiến cho nó được trả lại trong ô cuối cùng, được lọc, trả về số .filter().

Bạn cũng sẽ nhận thấy tôi đã đặt tên theo các phương pháp trên các thành phần List để ánh xạ qua các mục danh sách. Điều này là do đó, các hàm chỉ được khai báo một lần khi thành phần gắn kết và không được redeclared mỗi lần tái tạo thành phần. Tôi nghĩ rằng nó cũng đọc một chút đẹp hơn và bổ sung thêm ngữ nghĩa cho mã.

Chỉnh sửa: Tôi nhận thấy bạn đang sử dụng Babel nên tôi ES6 đã lên một chút. < 3 ES6.

3

Rõ ràng có nhiều cách để đạt được những gì bạn muốn.

Nhưng đây là một ví dụ về cách cá nhân tôi sẽ bố trí một giao diện người dùng đơn giản như thế.Tôi đã gỡ bỏ API gọi để cung cấp một mẫu hoàn toàn khả thi trong CodePen dưới

class Nav extends React.Component { 

    constructor() { 
    super(); 

    this.state = { 
     categories: [ 
     { title: 'First Category', id: 0 }, 
     { title: 'Second Category', id: 1 }, 
     { title: 'Third Category', id: 2 } 
     ], 
     items: [ 
     { title: 'Item 1', id: 0, category: { id: 0 } }, 
     { title: 'Item 2', id: 1, category: { id: 0 } }, 
     { title: 'Item 3', id: 2, category: { id: 0 } }, 
     { title: 'Item 4', id: 3, category: { id: 1 } }, 
     { title: 'Item 5', id: 4, category: { id: 1 } }, 
     { title: 'Item 6', id: 5, category: { id: 2 } }, 
     { title: 'Item 7', id: 6, category: { id: 2 } } 
     ], 
     selectedCategoryId: null 
    }; 

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

    onSelectCategory(id) { 
    this.setState({ 
     selectedCategoryId: id 
    }); 
    } 

    render() { 
    const { categories, items, selectedCategoryId } = this.state; 
    const deafultCategory = _.first(categories); 
    const selectedCategory = _.find(categories, i => i.id === selectedCategoryId) || deafultCategory;  
    return (
     <div> 
     <CategoryFilter categories={categories} onSelectCategory={this.onSelectCategory} /> 
     <ItemList items={items} selectedCategory={selectedCategory} /> 
     </div> 
    ); 
    } 
} 

var CategoryFilter = ({ categories, onSelectCategory}) => { 
    const links = categories.map(i => (
    <div key={i.id}> 
     <a href="#" onClick={() => onSelectCategory(i.id)}> 
     { i.title } 
     </a> 
    </div> 
)); 
    return (
    <div> 
     { links } 
    </div> 
) 
}; 

var ItemList = ({items, selectedCategory}) => { 
    const currentItems = items 
    .filter(i => i.category.id === selectedCategory.id) 
    .map(i => (
     <div key={i.id}> 
     { i.title } 
     </div> 
    )); 
    return (
    <div> 
     { currentItems } 
    </div> 
); 
}; 


ReactDOM.render(<Nav />, document.getElementById("app")); 

http://codepen.io/chadedrupt/pen/pbNNVO

Hy vọng rằng nó là khá giải thích.

Lưu ý. Sử dụng rất nhiều công cụ ES6 như tôi nghĩ rằng nó thực sự có giá trị học tập vì nó làm cho mọi thứ dễ chịu hơn rất nhiều. Cũng trộn lẫn một chút của Underscore/Lodash trong là tốt.

Các vấn đề liên quan