You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Pokémon库存从首页到详情页的动态同步与更新?

实现Pokémon库存的本地存储与动态更新

核心思路

用localStorage存储每个Pokémon的库存数据,设计如下结构:

// localStorage中key为'pokemonStocks',值为JSON对象
{
  "bulbasaur": 10,
  "charmander": 15,
  ...
}

通过Pokémon名称作为key快速读写库存,同时实现数据持久化保存。


步骤1:封装库存读写工具函数

创建utils/storage.js文件(或直接在组件内定义),统一处理localStorage操作:

// 获取指定Pokémon的库存,默认值为10
export const getPokemonStock = (pokemonName) => {
  const stocks = JSON.parse(localStorage.getItem('pokemonStocks')) || {};
  return stocks[pokemonName] || 10;
};

// 保存指定Pokémon的库存
export const savePokemonStock = (pokemonName, stock) => {
  const stocks = JSON.parse(localStorage.getItem('pokemonStocks')) || {};
  stocks[pokemonName] = parseInt(stock);
  localStorage.setItem('pokemonStocks', JSON.stringify(stocks));
};

步骤2:修改首页动态显示库存

替换静态库存为从localStorage读取的实时数据:

import { getPokemonStock } from './utils/storage';

export default function Homepage() {
    const [pokemons, setPokemons] = useState([])
    const [query, setQuery] = useState("")
    const [search, setSearch] = useState("")
    let navigate = useNavigate();

    // 原有getPokemons、searchPokemon、useEffect逻辑不变

    // 监听localStorage变化,实时更新首页库存
    useEffect(() => {
        const handleStorageUpdate = () => {
            setPokemons([...pokemons]);
        };
        window.addEventListener('storage', handleStorageUpdate);
        return () => window.removeEventListener('storage', handleStorageUpdate);
    }, [pokemons]);

    return (
        <div className="app-container">
            <h1 className="title">Stok Pokémon</h1>
            <div className="">
                <img src={searchIcon} className="search-icon" />
                <input type="text"
                    className="search-box"
                    placeholder="Cari Pokémon"
                    onChange={(e) => setQuery(e.target.value)}
                    onKeyUp={searchPokemon} />
            </div>
            <div className="row">
                <div className="col">
                    <Table className="table d-flex row">
                        <thead>
                            <tr className="d-flex justify-content-between th-border">
                                <th scope="col">Nama</th>
                                <th scope="col" className="d-flex text-right">Stok</th>
                            </tr>
                        </thead>
                        <tbody>
                            {
                                pokemons.filter(pokemon => pokemon.name.toLowerCase().includes(query)).map((pokemon, i) => {
                                    const stock = getPokemonStock(pokemon.name);
                                    return (
                                        <tr className="d-flex justify-content-between" key={i + 1}>
                                            <td className="table-link" onClick={() => {
                                                navigate(`/pokemon/${pokemon.name}`)
                                            }} style={{ textTransform: 'capitalize' }}>{pokemon.name}</td>
                                            <td className="pokemon-stock">{stock} pcs</td>
                                        </tr>
                                    )
                                })
                            }
                        </tbody>
                    </Table>
                </div>
            </div>
        </div >
    )
}

步骤3:修改详情页实现库存更新与展示

调整详情页,动态读取库存并给模态框添加保存逻辑:

import { getPokemonStock, savePokemonStock } from './utils/storage';

function UpdateStockModal(props) {
    const [input, setInput] = useState({
        pcs: 0,
        lusin: 0
    })

    const handleChange = e => {
        let newValue = {
            ...input,
            [e.target.name]: e.target.value,
        };
        setInput(newValue);
    };

    // 计算总库存并触发保存
    const handleSave = () => {
        const totalStock = parseInt(input.pcs) + parseInt(12 * input.lusin);
        props.onSave(totalStock);
        props.onHide();
    };

    return (
        <Modal
            {...props}
            size="lg"
            aria-labelledby="contained-modal-title-vcenter"
            className="modal"
            centered
        >
            <Modal.Header closeButton>
                <Modal.Title className="modal-title">
                    Update stock
                </Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <h4>Masukkan jumlah stok yang tersedia di rak saat ini.</h4>
                <Container>
                    <Row>
                        <Col>Kemasan</Col>
                        <Col>Jumlah</Col>
                        <Col>Stok</Col>
                    </Row>
                    <Row className="modal-table-body">
                        <Col >Pcs</Col>
                        <Col className="d-flex align-items-center">1 x <Form.Control className="modal-input pcs" type="text" name="pcs" value={input.pcs} onChange={handleChange} /> = </Col>
                        <Col>{input.pcs}</Col>
                    </Row>
                    <Row className="modal-table-body">
                        <Col>Lusin</Col>
                        <Col className="d-flex align-items-center">12 x <Form.Control name="lusin" className="modal-input lusin" type="text" value={input.lusin} onChange={handleChange} /> = </Col>
                        <Col>{12 * input.lusin}</Col>
                    </Row>
                    <Row className="modal-table-body">
                        <Col>Total Stok <span>(dalam pcs)</span></Col>
                        <Col>Lusin</Col>
                        <Col>{parseInt(12 * input.lusin) + parseInt(input.pcs)}</Col>
                    </Row>
                </Container>
            </Modal.Body>
            <Modal.Footer>
                <Button
                    variant="primary"
                    onClick={handleSave} >
                    Simpan
                </Button>
                <Button
                    variant="secondary"
                    onClick={props.onHide}>
                    Batal
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

export default function PokemonDetail() {
    let navigate = useNavigate();
    let { name } = useParams()
    const [modalShow, setModalShow] = React.useState(false);
    const [currentStock, setCurrentStock] = useState(getPokemonStock(name));

    // 保存库存并更新页面显示
    const handleSaveStock = (newStock) => {
        savePokemonStock(name, newStock);
        setCurrentStock(newStock);
        // 触发localStorage事件,通知首页更新
        window.dispatchEvent(new Event('storage'));
    };

    return (
        <div className="pokemon-detail-page">
            <div className="pokemon-detail_button-group">
                <Button variant="outline-light" className="prev-button" onClick={() => {
                    navigate('/')
                }}><img src={prevPageIcon}></img>Stok Pokémon</Button>
                <Button className="update-stock-button" onClick={() => setModalShow(true)}>Update Stok</Button>
            </div>
            <p className="pokemon-detail-title" style={{ textTransform: 'capitalize' }}>{name}</p>
            <div className="pokemon-detail-subtitle">
                <p className="pokemon-detail-sub1">Sisa Stok</p>
                <p className="pokemon-detail-sub2">{currentStock} pcs</p>
            </div>
            <div className="pokemon-detail-history">
                <p className="pokemon-detail-history1">Riwayat Stok</p>
                <p className="pokemon-detail-history2">Satuan stok dalam pcs</p>
            </div>
            <div>
                <Table className='col-xs-12 mt-4' responsive>
                    <thead>
                        <tr className="th-border ">
                            <th scope="col">Waktu</th>
                            <th scope="col">Kegiatan</th>
                            <th scope="col">Catatan</th>
                            <th scope="col">Jumlah</th>
                            <th scope="col">Stok</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr className="align-items-center">
                            <td className="">2 Apr 2021, 08:00</td>
                            <td className="table-link">Update Stok</td>
                            <td className="">"Stok Awal"</td>
                            <td className="table-count-stock">+10</td>
                            <td className="table-bold">{currentStock}</td>
                        </tr>
                    </tbody>
                </Table>
            </div>

            <UpdateStockModal
                show={modalShow}
                onHide={() => setModalShow(false)}
                onSave={handleSaveStock}
            />
        </div>
    )
}

额外优化建议

  1. 输入校验:在模态框中添加数字输入校验,避免非数字值导致计算错误
  2. 历史记录扩展:可以修改localStorage结构,给每个Pokémon添加库存历史数组,记录每次更新的时间、数量
  3. 批量初始化:首次加载时,可批量给所有从PokeAPI获取的Pokémon设置默认库存

内容的提问来源于stack exchange,提问作者Flo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 03:11:51