基于 Dropdown 的 React Effector 状态管理
React Effector State Management Based on Dropdown
我正在使用 React Hooks 和 Effector 尝试将数据渲染到卡片上。数据将从 React-Select
下拉列表(代表不同的国家/地区)中提取出来,其想法是用户将能够根据这些国家/地区添加 select 离子,有点像 TODO 应用程序。
但是,我发现每当我返回到上一个下拉列表 selection 时,数据都不会保存。我正在使用 effector
、hooks
、react-select
和 react-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>
);
}
这是一个示例图片。基本上,在 select 从下拉列表中选择一个国家(比如英格兰)后,我可以添加一个玩家,它会在卡片上呈现 - 然而,当我 select 一个不同的国家并返回之前的国家时,添加的玩家消失。任何想法如何解决这个问题?我现在正在研究 onAddButtonClick()
函数...
我不是 Effector 方面的专家,但我认为像大多数状态管理器一样,它依赖于状态不被改变,这就是你在推到 playersStore
时所做的。使用像我下面描述的减速器是正确的解决方案。
此外,我对 items
应该是什么感到有点困惑,因为据我所知,它是从商店和状态派生的,因此它本身不应该是状态.
最后,useEffect
的使用对我来说没有意义。通常这用于在渲染时触发动作。你真正想在这里做的要简单得多......即只获取当前状态并正确渲染它。
我对其进行了一些简化,以删除不相关的内容。
基本上是这样的。商店是国家名称到玩家列表的映射。当你需要对此进行更改时,你可以使用这个 reducer,它 returns 一个全新的对象,具有旧对象的所有状态,但是添加玩家的国家也是一个新数组,与旧内容,加上额外的项目。
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>
</>
);
};
我正在使用 React Hooks 和 Effector 尝试将数据渲染到卡片上。数据将从 React-Select
下拉列表(代表不同的国家/地区)中提取出来,其想法是用户将能够根据这些国家/地区添加 select 离子,有点像 TODO 应用程序。
但是,我发现每当我返回到上一个下拉列表 selection 时,数据都不会保存。我正在使用 effector
、hooks
、react-select
和 react-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>
);
}
这是一个示例图片。基本上,在 select 从下拉列表中选择一个国家(比如英格兰)后,我可以添加一个玩家,它会在卡片上呈现 - 然而,当我 select 一个不同的国家并返回之前的国家时,添加的玩家消失。任何想法如何解决这个问题?我现在正在研究 onAddButtonClick()
函数...
我不是 Effector 方面的专家,但我认为像大多数状态管理器一样,它依赖于状态不被改变,这就是你在推到 playersStore
时所做的。使用像我下面描述的减速器是正确的解决方案。
此外,我对 items
应该是什么感到有点困惑,因为据我所知,它是从商店和状态派生的,因此它本身不应该是状态.
最后,useEffect
的使用对我来说没有意义。通常这用于在渲染时触发动作。你真正想在这里做的要简单得多......即只获取当前状态并正确渲染它。
我对其进行了一些简化,以删除不相关的内容。
基本上是这样的。商店是国家名称到玩家列表的映射。当你需要对此进行更改时,你可以使用这个 reducer,它 returns 一个全新的对象,具有旧对象的所有状态,但是添加玩家的国家也是一个新数组,与旧内容,加上额外的项目。
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>
</>
);
};