React 官网 MAIN CONCEPTS中最后Thinking in React案例 TS 实现
这篇文章主要展示的是React官方文档中 MAIN CONCEPTS 最后一节 Thinking in React的案例 TS的写法。本人也是 React TS初学者,如有问题欢迎指出,共勉!😘
源码中使用的是Create React App构建的项目,同时使用了Storybook展示代码效果
效果
这个案例总共分了 FilterableProductTable
最外层 SearchBar
搜索栏 ProductTable
商品展示表格 ProductCategoryRow
商品展示分组 ProductRow
每个商品行 五个组件
代码
FilterableProductTable
/**
* filterable product table
*/
export default class FilterableProductTable
extends React.Component<{products: Product[]}> {
state = {
filterText: '',
inStockOnly: false,
};
/**
* constructor
* @param {{products: any}} props
*/
constructor(props: {products: Product[]}) {
super(props);
this.state = {
filterText: '',
inStockOnly: false,
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInStockChange = this.handleInStockChange.bind(this);
}
/**
* handle filter text change
* @param {string} filterText
*/
handleFilterTextChange(filterText: string) {
this.setState({
filterText: filterText,
});
}
/**
* handle in stock change
* @param {boolean} inStockOnly
*/
handleInStockChange(inStockOnly: boolean) {
this.setState({
inStockOnly: inStockOnly,
});
}
/**
* render
* @return {JSX.Element}
*/
render(): JSX.Element {
return (
<div>
<SearchBar
filterText={this.state.filterText}
inStockOnly = {this.state.inStockOnly}
onFilterTextChange={this.handleFilterTextChange}
onInStockChange={this.handleInStockChange}
/>
<ProductTable
products={this.props.products}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
/>
</div>
);
}
}
这一个组件就是这个案例的最外层,包含了 SearchBar
及 ProductTable
这个组件的内容主要包括:
- props, 需要传入一个商品数组 - Product[] 这个 Product是我自己定义的一个 interface
export interface Product {
category: string,
price: string,
stocked: boolean,
name: string,
}
- state, 包含两个属性
filterText
inStockOnly
分别代表搜索栏的过滤字符串与是否是库存的标识 - 两个 Change 函数用于传递给,主要功能是对 state 的赋值
- 最后的 render() 函数用于返回 JSX.Element
SearchBar
/**
* search bar
*/
class SearchBar extends React.Component<
{
filterText: string,
inStockOnly: boolean,
onFilterTextChange: Function,
onInStockChange: Function,
}
> {
/**
* constructor
* @param {{
* filterText: string,
* inStockOnly: boolean,
* onFilterTextChange: Function,
* onInStockChange: Function,
* }} props
*/
constructor(props: {
filterText: string,
inStockOnly: boolean,
onFilterTextChange: Function,
onInStockChange: Function,
}) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInStockChange = this.handleInStockChange.bind(this);
}
/**
* handle filter text change
* @param {{target: {value: string}}} e
*/
handleFilterTextChange(e: {target: {value: string}}) {
this.props.onFilterTextChange(e.target.value);
}
/**
* handle in stock change
* @param {{target: {checked: boolean}}} e
*/
handleInStockChange(e: {target: {checked: boolean}}) {
this.props.onInStockChange(e.target.checked);
}
/**
* @return {JSX.Element}
*/
render(): JSX.Element {
return (
<form>
<input
type="text"
placeholder="Search..."
value={this.props.filterText}
onChange={this.handleFilterTextChange}
/>
<p>
<input
type="checkbox"
checked={this.props.inStockOnly}
onChange={this.handleInStockChange}
/>
{' '}
Only show products in stock
</p>
</form>
);
}
}
SearchBar 这个组件即案例中的搜索栏,其包含了四个需要传入的 props 参数
- filterText 搜索输入框的字符串
- inStockOnly checkbox多选框选择状态
- onFilterTextChange / onInStockChange 即父级传入的两个函数用于在这个组件输入框或多选框发生改变时改变父级存储的两个状态
ProductTable | ProductCategoryRow | ProductRow
/**
* product table
*/
class ProductTable extends React.Component<{
products: Product[], filterText: string, inStockOnly: boolean}> {
/**
* @return {JSX.Element}
*/
render(): JSX.Element {
const filterText = this.props.filterText;
const inStockOnly = this.props.inStockOnly;
const rows: JSX.Element[] = [];
let lastCategory: string = '';
this.props.products.forEach((product) => {
if (product.name.indexOf(filterText) === -1) {
return;
}
if (inStockOnly && !product.stocked) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category} />,
);
}
rows.push(
<ProductRow
product={product}
key={product.name}
/>,
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
}
/**
* product category row
*/
class ProductCategoryRow extends
React.Component<{category: string, key: string}> {
/**
* render
* @return {JSX.Element}
*/
render(): JSX.Element {
const category = this.props.category;
return (
<tr>
<th colSpan={2}>
{category}
</th>
</tr>
);
}
}
/**
* product row
*/
class ProductRow extends React.Component<{product: Product, key: string}> {
/**
* render
* @return {JSX.Element}
*/
render() {
const product = this.props.product;
const name = product.stocked ?
product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
}
上面展示的三个组件的代码也就是案例中,搜索栏下方的商品展示部分,总入口为 ProductTable
组件
ProductTable
需要接收来自父组件 FilterableProductTable
的三个参数,即展示的商品,搜索框的文本内容及是否展示库存中的商品状态,此时由父级传入的商品是全量的,而做过滤就是在ProductTable
这个组件进行,具体在 ProductTable
组件的 render()
函数中,而另外两个子组件 ProductCategoryRow
ProductRow
则用于视图展示
我的源码最后在 storybook 中进行展示
import React from 'react';
import FilterableProductTable, {Product} from './FilterableProductTable';
import {ComponentMeta, ComponentStory} from '@storybook/react';
const PRODUCTS: Product[] = [
{category: 'Sporting Goods', price: '$49.99',
stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99',
stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'},
];
export default {
title: 'Test/FilterableProductTable',
component: FilterableProductTable,
args: {
products: PRODUCTS,
},
} as ComponentMeta<typeof FilterableProductTable>;
const Template: ComponentStory<typeof FilterableProductTable> =
(args) => <FilterableProductTable {...args} />;
export const Primary = Template.bind({});
Primary.args = {
products: PRODUCTS,
};
以上便是这篇文章的所有内容,感谢各位的阅读❤️