Reactの流儀の実装例

Reactの流儀の実装例を書きました。
内容としては拙いものがありますが、記録として残しておきます。

```index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

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

function SearchBar(props){
    return (
        <div>
            <input type='text' placeholder='Search...' onChange={props.handleInputSearchText} value={props.searchText}/>
            <br />
            <input type='checkbox' checked={props.onlyStocked} onChange={props.handleOnlyStocked} />Only show products in stock
        </div>
    );
}

function ProductTable(props){
    const categories = [];
    products.forEach(product => {
        if(!categories.includes(product.category)){
            categories.push(product.category);
        }
    });

    const reactDom = [];
    categories.forEach(category => {
        reactDom.push(<ProductCategoryRow category={category} />);
        products.forEach(product => {
            if(category === product.category){
                if(props.searchText === '' || product.name.includes(props.searchText)){
                    if(props.onlyStocked === true){
                        if(product.stocked === true){
                            reactDom.push(<ProductRow name={product.name} price={product.price} stocked={product.stocked}/>);
                        }
                    }else{
                        reactDom.push(<ProductRow name={product.name} price={product.price} stocked={product.stocked}/>);
                    }
                }
            }
        });
    });

    return (
        <table>
            <thead>
                <tr>
                    <th style={{textAlign: 'left'}}>Name</th>
                    <th style={{textAlign: 'left'}}>Price</th>
                </tr>
            </thead>
            <tbody>
                {reactDom}
            </tbody>
        </table>
    );
}

function ProductCategoryRow(props){
    return (
        <tr>
            <td><b>{props.category}</b></td>
        </tr>
    );
}

function ProductRow(props){
    if(props.stocked){
        return (
            <tr>
                <td>{props.name}</td>
                <td> {props.price}</td>
            </tr>
        );
    }else{
        return (
            <tr>
                <td style={{color: 'red'}}>{props.name}</td>
                <td> {props.price}</td>
            </tr>
        );
    }
}

class FilterableProductTable extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            onlyStocked: false,
            searchText: ''
        }
        this.handleOnlyStocked = this.handleOnlyStocked.bind(this);
        this.handleInputSearchText = this.handleInputSearchText.bind(this);
    }

    handleOnlyStocked(){
        this.setState({onlyStocked: !this.state.onlyStocked});
    }

    handleInputSearchText(event){
        this.setState({searchText: event.target.value});
    }

    render() {
        return (
            <div>
                <SearchBar
                    onlyStocked={this.state.onlyStocked}
                    handleOnlyStocked={this.handleOnlyStocked}
                    handleInputSearchText={this.handleInputSearchText}
                    searchText={this.state.searchText}
                />
                <ProductTable
                    onlyStocked={this.state.onlyStocked}
                    searchText={this.state.searchText}
                />
            </div>
        );
    }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FilterableProductTable />)
```