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)
这是我到目前为止在我的网页上的截图.一切都只是在屏幕上转储.蓝色链接是类别.
我相信我有适合你的版本.为了清晰起见,我更改了一些语法和变量/ 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.