【问题标题】:React Effector State Management Based on Dropdown基于 Dropdown 的 React Effector 状态管理
【发布时间】:2020-11-21 15:04:13
【问题描述】:

我正在使用 React Hooks 和 Effector 尝试将数据渲染到卡片上。数据将从React-Select 下拉列表(代表不同的国家/地区)中删除,其想法是用户将能够根据这些国家/地区添加选择,有点像 TODO 应用程序。

但是,我发现每当我返回上一个下拉选择时,数据都不会保存。我正在使用effectorhooksreact-selectreact-jss 进行造型。团队从React-Select 组件作为道具传递。

const PlayerComponent = ({ containerStyles, team }) => {

    const [items, setItems] = useState([]);

    const teamsStore = useStore(testTeams);
    const playersStore = useStore(testPlayers);

    useEffect(() => {

        const playersByTeam = playersStore.filter(
            (player) =>
                player.teamId ===
                teamsStore.find(function (t) {
                    return t.label === team;
                }).teamId
        );
    
        setItems(
            playersByTeam.map(function (player) {
                let players = { name: player.name };
                return players;
            })
        );
    }, [playersStore,team,teamsStore]);

    function onAddButtonClick() {
        setItems((prev) => {
            const newItems = [...prev];

            newItems.push({
                name: `Player ${newItems.length + 1}`
            });

            playersStore.push({
                name: `Player ${newItems.length + 1}`
            });

            return newItems;
        });
    }

    function renderAddButton() {
        return (
            <Row
                horizontal='center'
                vertical='center'
                onClick={onAddButtonClick}
            >
                Add Player
            </Row>
        );
    }

    return (
        <CardComponent
            containerStyles={containerStyles}
            title={team}
            items={[
                <Row horizontal='space-between' vertical='center'>
                    <span>
                        Create new player
                    </span>
                    {renderAddButton()}
                </Row>,
                ...items.map((item, index) => (
                    <Player index={index} item={item} />
                ))
            ]}
        />
    );
};

这是我的Player 组件,每个代表一行:

function Player({item = {} }) {
    return (
        <Row horizontal='space-between' vertical='center'>
            <Row>
                <span>{item.name}</span>
            </Row>
        </Row>
    );
}

这是一个示例图像。基本上,在从下拉列表中选择一个国家(比如英格兰)后,我可以添加一个球员,它会呈现在卡片上——但是,当我选择一个不同的国家并返回前一个国家时,添加的球员就会消失。任何想法如何解决这个问题?我现在正在为onAddButtonClick() 函数而烦恼...

【问题讨论】:

  • team国家吗?
  • @dpwrussell 是的 team 确实是国家!

标签: javascript reactjs react-hooks react-select effector


【解决方案1】:

我不是 Effector 方面的专家,但我认为与大多数状态管理器一样,它依赖于状态不会发生变异,这就是您在推送到 playersStore 时所做的。使用我下面描述的减速器是正确的解决方案。

另外,我对 items 应该是什么感到有些困惑,因为据我所知,它是从 store 和 state 派生的,因此本身不应该是 state。

最后,useEffect 的使用对我来说没有意义。通常这用于在渲染时触发动作。您真正想要在这里做的事情要简单得多......即只需获取当前状态并正确呈现它。

我已经稍微简化了它以删除不相关的内容。

基本上它是这样工作的。该商店是国家名称到玩家列表的映射。当您需要对此进行更改时,您可以使用这个 reducer,它返回一个全新的对象,其中包含旧对象的所有状态,但是添加玩家的国家/地区也是一个新数组,具有旧内容,加上额外的项目。

const onAddPlayer = (state, payload) => {
  const newState = { ...state };
  newState[payload["country"]] = [
    ...newState[payload["country"]],
    payload["player"]
  ];
  return newState;
};

// Create the store, registering the reducer for the addPlayer event.
const playersByCountry = createStore(initialStore).on(addPlayer, onAddPlayer);

此外,还有一个useState 变量来跟踪当前选定的国家/地区。

完整代码,也可上codesandbox.io

import React, { useState } from "react";
import { createStore, createEvent } from "effector";
import { useStore } from "effector-react";
import "./styles.css";
import Select from "react-select";

const initialStore = {
  France: [],
  Germany: ["Hans", "Kurt"],
  Italy: ["Francesca"]
};

const addPlayer = createEvent();

const onAddPlayer = (state, payload) => {
  const newState = { ...state };
  newState[payload["country"]] = [
    ...newState[payload["country"]],
    payload["player"]
  ];
  return newState;
};

const playersByCountry = createStore(initialStore).on(addPlayer, onAddPlayer);

export default () => {
  const pbc = useStore(playersByCountry);
  const options = Object.keys(pbc).map((item) => ({
    value: item,
    label: item
  }));
  const [country, setCountry] = useState(options[0]);
  const [playerInput, setPlayerInput] = useState("");
  const players = pbc[country["value"]];

  // When selector is changed, clear the input and set the
  // state `country` which is the current selection
  const handleCountryChange = (event) => {
    setCountry(event);
    setPlayerInput("");
  };

  // Controlled input state update
  const handlePlayerInputChange = (event) => {
    setPlayerInput(event.target.value);
  };

  // Pressing submit clears the input and adds the player
  // to the store
  const handlePlayerSubmit = (event) => {
    addPlayer({
      country: country["value"],
      player: playerInput
    });
    setPlayerInput("");
    event.preventDefault();
  };

  return (
    <>
      <Select
        defaultValue={country}
        options={options}
        onChange={handleCountryChange}
      />
      <div>
        <h1>Existing Players</h1>
        <ul>
          {players.map((player) => (
            <li>{player}</li>
          ))}
        </ul>
      </div>
      <form onSubmit={handlePlayerSubmit}>
        <label>
          Player:
          <input
            type="text"
            name="player"
            value={playerInput}
            onChange={handlePlayerInputChange}
          />
        </label>
        <input type="submit" value="Add" />
      </form>
    </>
  );
};

【讨论】:

    猜你喜欢
    • 2022-10-20
    • 2020-07-13
    • 2014-11-27
    • 1970-01-01
    • 2019-11-28
    • 2017-11-26
    • 1970-01-01
    • 2018-08-19
    • 2021-05-03
    相关资源
    最近更新 更多