单击类别时如何显示不同的项目列表

ger*_*res 12 html javascript reactjs

我是React JS的新手,我需要构建这个简单的UI.我基本上有一个类别列表,如果我点击一个类别,项目列表将显示在该类别下.如果我点击另一个类别,它将隐藏项目列表.

我提供了两个API,一个包含类别的JSON,另一个包含项目.

我已经设法从API中获取数据并将其吐出DOM.但是我发现很难将组件拼凑在一起,只在单击类别时才显示正确的项目.

我正在使用Babel来转换我的JSX语法并使用axios来获取数据.目前我的页面只会吐出所有项目和所有类别.理解国家对我来说很难.

对新手Reactjs更精简的任何建议?谢谢!

我的两个API可以在我的代码中找到,因为我没有足够的重复点来发布链接.

我的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"));
Run Code Online (Sandbox Code Playgroud)

这是我到目前为止在我的网页上的截图.一切都只是在屏幕上转储.蓝色链接是类别.

当前网页的屏幕截图

jay*_*bee 8

我相信我有适合你的版本.为了清晰起见,我更改了一些语法和变量/ prop名称,并添加了解释更改的注释

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'));
Run Code Online (Sandbox Code Playgroud)

这里过滤的两个关键部分是数组上的.filter().some()方法.

return items.filter(item => {
  return item.categories.some(category => {
    return category.id === selectedCategoryId;
  });
})
Run Code Online (Sandbox Code Playgroud)

这是说:迭代所有的items.对于每一个item,迭代它categories并检查它们id的任何一个是否与它相同selectedCategoryId.如果其中之一是,该.some()语句将返回true导致该item.filter()返回true,使其最终,过滤,阵列,在获得返回.filter()返回.

您还会注意到我在List组件上创建了用于映射列表项的命名方法.这样,函数只在组件安装时才会声明一次,并且每次组件重新渲染时都不会重新声明.我认为它也读得更好,并为代码添加了更多的语义.

编辑:我注意到你正在使用Babel,所以我ES6了一下.<3 ES6.