【React入门学习】13.React哲学

122 阅读4分钟

React 哲学

从设计稿开始

假设我们已经有了一个返回 JSON 的 API,以及设计师提供的组件设计稿。如下所示:

image.png

该 JSON API 会返回以下数据:

[
  {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"}
];

一.划分组件层级

image.png

  1. FilterableProductTable (橙色):  是整个示例应用的整体
  2. SearchBar (蓝色):  接受所有的用户输入
  3. ProductTable (绿色):  展示数据内容并根据用户输入筛选结果
  4. ProductCategoryRow (天蓝色):  为每一个产品类别展示标题
  5. ProductRow (红色):  每一行展示一个产品
  • FilterableProductTable

    • SearchBar

    • ProductTable

      • ProductCategoryRow
      • ProductRow

第二步:用 React 创建一个静态版本,自下由上创建静态版本结构

创建 分类组件(分类名称)

image.png

创建商品内容组件

image.png

image.png

创建商品展示区

要注意:创建新元素时,需要给它设置一个特殊的 key 属性,key 帮助 React 识别哪些元素改变了,比如被添加或删除,详情请看react.docschina.org/docs/lists-…

image.png

创建商品搜索区

image.png

创建父组件,引入子组件

image.png

 const PRODUCTS = [
            { 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' }
        ];

确定 state 放置的位置

对于应用中的每一个 state:

  • 找到根据这个 state 进行渲染的所有组件。
  • 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
  • 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
  • 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。

根据以上策略重新考虑我们的示例应用:

  • ProductTable 需要根据 state 筛选产品列表。SearchBar 需要展示搜索词和复选框的状态。
  • 他们的共同所有者是 FilterableProductTable
  • 因此,搜索词和复选框的值应该很自然地存放在 FilterableProductTable 组件中。

很好,我们已经决定把这些 state 存放在 FilterableProductTable 组件中。首先,将实例属性 this.state = {filterText: '', inStockOnly: false} 添加到 FilterableProductTable 的 constructor 中,设置应用的初始 state;接着,将 filterText 和 inStockOnly 作为 props 传入 ProductTable 和 SearchBar;最后,用这些 props 筛选 ProductTable 中的产品信息,并设置 SearchBar 的表单值。

你现在可以看到应用的变化了:将 filterText 设置为 "ball" 并刷新应用,你能发现表格中的数据已经更新了。

 class ProductCategoryRow extends React.Component {
            render() {
                const category = this.props.category;
                return (
                    <tr>
                        <th colSpan="2">
                            {category}
                        </th>
                    </tr>
                );
            }
        }

        // 创建商品内容组件
        class ProductRow extends React.Component {
            render() {
                // 父级传值,使用this.props读取数据
                const product = this.props.product;
                // stocked:false 无库存。界面显示为红色
                const name = product.stocked ?
                    product.name :
                    <span style={{ color: 'red' }}>
                        {product.name}
                    </span>;

                return (
                    <tr>
                        <td>{name}</td>
                        <td>{product.price}</td>
                    </tr>
                );
            }
        }
        // 创建商品展示区
        class ProductTable extends React.Component {
            render() {
                /*
                    filterText 搜索框文本
                    inStockOnly 存货状态
                */
                const filterText = this.props.filterText;
                const inStockOnly = this.props.inStockOnly;

                // 创建空元素
                const rows = [];
                let lastCategory = null;
                // 根据分类遍历数据
                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 属性
                                key={product.category} />
                        );
                    }
                    rows.push(
                        <ProductRow
                            product={product}
                            // 当你创建一个元素时必须包括一个特殊的 key 属性
                            key={product.name} />
                    );
                    // 遍历完成后 设置lastCategory为最后一个遍历的分类的值,避免重复push分类组件
                    lastCategory = product.category;
                });

                return (
                    <table>
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Price</th>
                            </tr>
                        </thead>
                        <tbody>{rows}</tbody>
                    </table>
                );
            }
        }

        // 创建商品搜索区组件
        class SearchBar extends React.Component {
            // 创建构造函数
            constructor(props) {
                /*
                    绑定两个函数 
                    handleFilterTextChange 监听搜索框文字内容变化
                    handleInStockChange 监听 存货按钮状态变化
                */
                super(props);
                // 在 JavaScript 中,class 的方法默认不会绑定 this。不绑定的话会出现 undefined
                this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
                this.handleInStockChange = this.handleInStockChange.bind(this);
            }



            handleFilterTextChange(e) {
                this.props.onFilterTextChange(e.target.value);
            }

            handleInStockChange(e) {
                this.props.onInStockChange(e.target.checked);
            }
            render() {
                /* 
                    由于这个组件的传值是由 该组件的父组件传值过来的 所以需要使用this.props获取
                    设置两个参数的作用:
                    1.接受 父组件 传值过来的数据
                    2.绑定组件内部元素的值,当组件内部元素状态或数据发生变化时,React会监听并同步给父组件,
                   实现 state 来实现交互效果
                */
                // const filterText = this.props.filterText;
                // const inStockOnly = this.props.inStockOnly;
                return (
                    <form>
                        <input
                            type="text"
                            placeholder="Search..."
                            value={this.props.filterText} 
                            // 监听搜索框内容变化调用函数实时改变FilterText的值
                            onChange={this.handleFilterTextChange}/>
                        <p>
                            <input
                                type="checkbox"
                                checked={this.props.inStockOnly}
                                onChange={this.handleInStockChange}
                            />
                            {' '}
                            Only show products in stock
                        </p>
                    </form>
                );
            }
        }

        // 创建FilterableProductTable父组件 引入搜索组件和商品组件作为子组件
        class FilterableProductTable extends React.Component {
            constructor(props) {
                // ES6语法 继承参数
                super(props);
                this.state = { filterText: '', inStockOnly: false }
                
                this.handleFilterTextChange=this.handleFilterTextChange.bind(this);
                this.handleInStockChange=this.handleInStockChange.bind(this);
            }
            handleFilterTextChange(filterText){
                this.setState({
                    filterText:filterText
                })
            }
            handleInStockChange(inStockOnly){
                this.setState({
                    inStockOnly:inStockOnly
                })
            }
            render() {
                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>
                );
            }
        }


        const PRODUCTS = [
            { 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' }
        ];

        ReactDOM.render(
            <FilterableProductTable products={PRODUCTS} />,
            document.getElementById('container')
        );