我自己尝试了一堆,并查看了Stack Overflow上的几个问题后,仍无法找到解决我特定问题的有效解决方案,因此希望有人可以引导我朝正确的方向前进。
我正在使用React.js挂钩,它有一个状态对象,该对象根据两个选择菜单和onChange处理程序更改状态(大小和颜色)。关于状态更改,除了设置选择菜单外,我还想导出要为selectedVariant
设置的数字ID,其中该ID来自options
状态对象的不同组合,而与顺序无关在其中被选中。在我的控件中,所有可能的组合(如{color: "Blue", size : "M" } -- mapped to --> 1234
都是已知的,可以放在Map,对象或数组等中,但是我不确定设置映射并有效响应此变化状态的最佳方法。我创建的setup和onChange看起来像这样,这是一个简单的codeandbox linked here,其中包含要演示的其他信息和下面的代码的游乐场
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"
}
]
}
]
}
这很可能导致每次状态更改时都必须执行类似js .find()
的操作,以获取variant [n] .id传递给setSelectedVariant
。这是最好的方法吗?
这是useReducer 的好用例吗 ?我还研究了JavaScript Map,该JavaScript允许您将对象设置为键,但是在这种情况下我无法使其工作。我愿意就不同的思考方式提出建议,但希望有人可以向我指出一些可以帮助我的想法/资源。
答案 0 :(得分:0)
以下代码获取您的变体数据,并为选择器和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>