javascript - 使用 React State 对象作为另一个值的键
问题描述
在我自己尝试了一堆之后,我还没有找到一个有效的解决方案来解决我的具体问题,并且在 Stack Overflow 上查看了几个问题,所以希望有人能引导我朝着正确的方向前进。
我正在使用 React.js 钩子有一个状态对象,它根据两个选择菜单和 onChange 处理程序更改状态(大小和颜色)。在状态更改时,除了设置选择菜单之外,我还想派生一个数字 ID 来设置selectedVariant
,其中 ID 来自options
状态对象的不同组合,无论它们被选择的顺序如何。所有可能的组合{color: "Blue", size : "M" } -- mapped to --> 1234
都是已知的,在我的控制下,可以放入映射、对象或数组等中,但我不确定设置映射和有效响应这种变化状态的最佳方法。我创建的设置和 onChange 看起来像这样,这里是一个简单的代码框,链接在这里,其中包含要演示的其他信息和下面代码的游乐场
const [options, setOptions] = useState({}); // based on this state ...
const [selectedVariant, setSelectedVariant] = useState(null); // how do I derive a value for this?
...
<select
value={options.size}
onChange={e => setOptions({ ...options, size: e.target.value })}
>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
</select>
<select
value={options.color}
onChange={e => setOptions({ ...options, color: e.target.value })}
>
<option value="Blue">Blue</option>
<option value="Gray">Gray</option>
</select>
...
当前状态可能是{size : "M", color : "Blue"}
等{color : "Gray", size : "XL"}
,具体取决于当前选择的内容和选择框的填充顺序。但是,我现在需要从当前选择的状态派生一个变体 ID,以了解使用这些属性的组合选择了哪个产品变体。
示例: {size : "M", color : "Blue"}
将从映射源派生1234
,然后将设置为setSelectedVariant(12345)
并成为新selectedVariant
状态。
{color : "Gray", size : "XL"}
(注意:不同的顺序但相同的键)将从映射源派生5678
,然后将被设置为setSelectedVariant(5678)
并成为新selectedVariant
状态。
更新: 示例映射: 这是一个示例映射对象,可以将它们映射以将变体与选项值相关联。
{
"variants": [
{
"id": 1234,
"title": "M / Blue",
"option_values": [
{
"name": "Size",
"value": "M"
},
{
"name": "Color",
"value": "Blue"
}
]
},
{
"id": 5678,
"title": "XL / Gray",
"option_values": [
{
"name": "Size",
"value": "XL"
},
{
"name": "Color",
"value": "Gray"
}
]
}
]
}
这很可能导致.find()
每次状态更改时都必须执行类似 js 的操作,以便获取 variant[n].id 以传递给setSelectedVariant
. 这是最好的方法吗?
这会是useReducer的一个很好的用例 吗?我还研究了 javascript Map,它允许您将对象设置为键,但我无法让它在这种情况下工作。我愿意接受有关不同思考方式的建议,但希望有人能指出一些想法/资源来帮助我。
解决方案
以下代码获取您的变体数据并为选择器生成数据,并且variantsByKey
可用于根据来自选择的值查找变体的 id。
可variantsSelects
用于选择并包含启用值以防止不存在的组合,例如Blue / XL
.
这是代码,我禁止显示console.logs,但您可以在项目中使用代码并使用它来更好地理解它。
const data = {
variants: [
{
id: 5,
title: 'Blue',
option_values: [
{
name: 'Color',
value: 'Blue',
},
],
},
{
id: 1234,
title: 'M / Blue',
option_values: [
{
name: 'Size',
value: 'M',
},
{
name: 'Color',
value: 'Blue',
},
],
},
{
id: 5678,
title: 'XL / Gray',
option_values: [
{
name: 'Size',
value: 'XL',
},
{
name: 'Color',
value: 'Gray',
},
],
},
],
};
const removeKey = (object, key) =>
Object.entries(object)
.filter(([k]) => k !== key)
.reduce((result, [key, value]) => {
result[key] = value;
return result;
}, {});
//special none value
const NONE = 'NONE';
const App = ({ data }) => {
const [
selectedVariants,
setSelectedVariants,
] = React.useState({});
//set the values for dropdowns
const variants = React.useMemo(
() =>
[
...data.variants
.map(({ option_values }) => option_values)
.flat()
.reduce(
(result, { name, value }) =>
result.set(
name,
(result.get(name) || []).concat(value)
),
new Map()
)
.entries(),
].map(([key, values]) => [key, [...new Set(values)]]),
[data.variants]
);
console.log('variants:', variants);
const variantsByKey = React.useMemo(
() =>
new Map(
data.variants.map(({ id, option_values }) => [
variants
.map(([key]) =>
option_values.find(({ name }) => name === key)
)
.filter((x) => x)
.map(({ name, value }) => `${name}::${value}`)
.join('++'),
id,
])
),
[data.variants, variants]
);
//selects with enabled value to disable non existant
// combinations
const variantsSelects = React.useMemo(() => {
const optionGroup = data.variants.map(
({ option_values }) =>
option_values
.map(({ name, value }) => [name, value])
.reduce(
(result, [key, value]) =>
result.set(key, value),
new Map()
)
);
const selected = Object.entries(selectedVariants);
return variants.map(([key, options]) => {
//selected options munus current option type
const sel = selected.filter(([k]) => k !== key);
return [
key,
options.map((option) => [
option,
optionGroup
.filter((variant) =>
sel.every(
([key, value]) => variant.get(key) === value
)
)
.some((v) => v.get(key) === option),
]),
];
});
}, [data.variants, selectedVariants, variants]);
console.log('variants by key', variantsByKey);
console.log('selects', variantsSelects);
const [variantId, setVariantId] = React.useState();
React.useEffect(() => {
const variantId = variantsByKey.get(
variants
.map(([key]) => key)
.filter((key) => selectedVariants[key])
.map((key) => `${key}::${selectedVariants[key]}`)
.join('++')
);
setVariantId(variantId);
}, [selectedVariants, variants, variantsByKey]);
const changeSelectedVariant = React.useCallback(
(value, key) =>
setSelectedVariants((current) =>
value !== NONE
? {
...current,
[key]: value,
}
: removeKey(current, key)
),
[]
);
return (
<div>
<h3>variant id: {variantId}</h3>
{variantsSelects.map(([key, values]) => (
<label key={key}>
{key}
<select
value={selectedVariants[key]}
onChange={(e) =>
changeSelectedVariant(e.target.value, key)
}
>
<option value={NONE}>Select option</option>
{values.map(([value, enabled]) => (
<option
value={value}
name={key}
key={value}
disabled={!enabled}
>
{value}
</option>
))}
</select>
</label>
))}
</div>
);
};
ReactDOM.render(
<App data={data} />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
推荐阅读
- google-bigquery - 根据条件/列值 BigQuery 创建分区
- android - 如何从手持式二维码扫描仪获取文本数据?
- r - 在 R 中将纳秒转换为 hh:mm:ss.ms
- javascript - 在 Visual Studio Code 中使用装饰器时出错
- http-live-streaming - IDM 不开始下载的 JWplayer 背后的技术细节是什么?
- mysql - 根据时间戳的最大列值
- amazon-s3 - Monitor S3 - 如果自最后一个文件写入后超过 5 分钟,则发送警报
- android - Flutter 无法处理方法调用 E/MethodChannel#flutter/platform(8279):
- wordpress - 我们可以在 WordPress 网站旁边有一个 ASP.NET Core 应用程序吗?
- ios - 集合视图规模流布局