我怎样才能实现不仅一个而且多个设置切换?
How can I implement not only one but multi setting toggles?
我使用 Shopify Polaris 的设置开关。https://polaris.shopify.com/components/actions/setting-toggle#navigation
而且我不仅要实现一个设置,还要实现多个设置 toggles.But 我不想总是重复相同的 handleToggle() 和值(contentStatus,textStatus),就像沙盒 A、B、C 下面那样。 ..
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
export default function SettingToggleExample() {
const [activeA, setActiveA] = useState(false);
const [activeB, setActiveB] = useState(false);
const handleToggleA = useCallback(() => setActiveA((active) => !active), []);
const handleToggleB = useCallback(() => setActiveB((active) => !active), []);
const contentStatusA = activeA ? "Deactivate" : "Activate";
const contentStatusB = activeB ? "Deactivate" : "Activate";
const textStatusA = activeA ? "activated" : "deactivated";
const textStatusB = activeB ? "activated" : "deactivated";
const useHandleToggle = (active, setActive) => {
const handleToggle = useCallback(() => setActive((active) => !active), []);
const contentStatus = active ? "Disconnect" : "Connect";
const textStatus = active ? "connected" : "disconnected";
handleToggle();
return [contentStatus, textStatus];
};
useHandleToggle(activeA, setActiveA);
return (
<>
<SettingToggle
action={{
content: contentStatusA,
onAction: handleToggleA
}}
enabled={activeA}
>
This setting is <TextStyle variation="strong">{textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: contentStatusB,
onAction: handleToggleB
}}
enabled={activeB}
>
This setting is <TextStyle variation="strong">{textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
https://codesandbox.io/s/vigorous-pine-k0dpib?file=/App.js
所以我想我可以使用自定义挂钩。但它不起作用。所以如果你给我一些建议会很有帮助。
我的第一次重构尝试是在公共处理程序上使用参数
const handleToggle = useCallback((which) => {
which === 'A' ? setActiveA((activeA) => !activeA)
: setActiveB((activeB) => !activeB)
},[])
...
<SettingToggle
action={{
content: contentStatusA,
onAction: () => handleToggle('A')
}}
enabled={activeA}
>
它的功能,但感觉有点幼稚。对于更多 React-ish,reducer 可能是可行的方法。
带减速机
这看起来更简洁,如果您需要更多开关,它肯定更可扩展。
function reducer(state, action) {
switch (action.type) {
case "toggleA":
const newValueA = !state.activeA;
return {
...state,
activeA: newValueA,
contentStatusA: newValueA ? "Deactivate" : "Activate",
textStatusA: newValueA ? "activated" : "deactivated"
};
case "toggleB":
const newValueB = !state.activeB;
return {
...state,
activeB: newValueB,
contentStatusB: newValueB ? "Deactivate" : "Activate",
textStatusB: newValueB ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
activeA: false,
activeB: false,
contentStatusA: "Activate",
contentStatusB: "Activate",
textStatusA: "deactivated",
textStatusB: "deactivated"
};
export default function SettingToggleExample() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<>
<SettingToggle
action={{
content: state.contentStatusA,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeA}
>
This setting is <TextStyle variation="strong">{state.textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: state.contentStatusB,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeB}
>
This setting is <TextStyle variation="strong">{state.textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
带有包装组件
子组件可以消除 'A' 和 'B' 后缀
function reducer(state, action) {
switch (action.type) {
case "toggle":
const newValue = !state.active;
return {
...state,
active: newValue,
contentStatus: newValue ? "Deactivate" : "Activate",
textStatus: newValue ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
active: false,
contentStatus: "Activate",
textStatus: "deactivated",
};
const ToggleWrapper = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<SettingToggle
action={{
content: state.contentStatus,
onAction: () => dispatch({ type: "toggle" })
}}
enabled={state.active}
>
This setting is <TextStyle variation="strong">{state.textStatus}</TextStyle>.
</SettingToggle>
)
}
export default function SettingToggleExample() {
return (
<>
<ToggleWrapper />
<ToggleWrapper />
</>
);
}
对每个切换使用简单的布尔值
如果您将活动状态对象合并到一个数组中,那么您可以动态更新任意数量的设置。这是一个可能看起来像的例子:
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
export default function SettingToggleExample() {
// define stateful array of size equal to number of toggles
const [active, setActive] = useState(Array(2).fill(false));
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive(prev => [...prev.slice(0,i), !prev[i], ...prev.slice(i+1)])
}, []);
return (
<>
{activeStatuses.map((isActive, index) =>
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
>
This setting is <TextStyle variation="strong">{isActive ? "activated" : "deactivated"}</TextStyle>.
</SettingToggle>
}
</>
);
}
当然,您以后可能希望为其中的每一个添加一个标签,因此最好在函数范围之外定义一个 defaultState 对象并用它替换 Array(2).fill(false)
。然后,除了布尔值 active
属性 之外,您还可以为每个切换设置一个字符串 label
属性,可以将其添加到 .map(...)
中每个切换的旁边。
为每个切换添加标签
根据您的跟进,这里是在 CodeSandbox 中也找到的实现,用于每个切换带有标签的状态(包括此处关于防止 link 衰减的答案):
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
const defaultState = [
{
isActive: false,
label: "A"
},
{
isActive: false,
label: "B"
},
{
isActive: false,
label: "C"
}
];
export default function SettingToggleExample() {
const [active, setActive] = useState(defaultState);
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive((prev) => [
...prev.slice(0, i),
{ ...prev[i], isActive: !prev[i].isActive },
...prev.slice(i + 1)
]);
}, []);
return (
<div style={{ height: "100vh" }}>
{active?.map(({ isActive, label }, index) => (
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
key={index}
>
This {label} is
<TextStyle variation="strong">
{isActive ? "activated" : "deactivated"}
</TextStyle>
.
</SettingToggle>
))}
</div>
);
}
我使用 Shopify Polaris 的设置开关。https://polaris.shopify.com/components/actions/setting-toggle#navigation
而且我不仅要实现一个设置,还要实现多个设置 toggles.But 我不想总是重复相同的 handleToggle() 和值(contentStatus,textStatus),就像沙盒 A、B、C 下面那样。 ..
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
export default function SettingToggleExample() {
const [activeA, setActiveA] = useState(false);
const [activeB, setActiveB] = useState(false);
const handleToggleA = useCallback(() => setActiveA((active) => !active), []);
const handleToggleB = useCallback(() => setActiveB((active) => !active), []);
const contentStatusA = activeA ? "Deactivate" : "Activate";
const contentStatusB = activeB ? "Deactivate" : "Activate";
const textStatusA = activeA ? "activated" : "deactivated";
const textStatusB = activeB ? "activated" : "deactivated";
const useHandleToggle = (active, setActive) => {
const handleToggle = useCallback(() => setActive((active) => !active), []);
const contentStatus = active ? "Disconnect" : "Connect";
const textStatus = active ? "connected" : "disconnected";
handleToggle();
return [contentStatus, textStatus];
};
useHandleToggle(activeA, setActiveA);
return (
<>
<SettingToggle
action={{
content: contentStatusA,
onAction: handleToggleA
}}
enabled={activeA}
>
This setting is <TextStyle variation="strong">{textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: contentStatusB,
onAction: handleToggleB
}}
enabled={activeB}
>
This setting is <TextStyle variation="strong">{textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
https://codesandbox.io/s/vigorous-pine-k0dpib?file=/App.js
所以我想我可以使用自定义挂钩。但它不起作用。所以如果你给我一些建议会很有帮助。
我的第一次重构尝试是在公共处理程序上使用参数
const handleToggle = useCallback((which) => {
which === 'A' ? setActiveA((activeA) => !activeA)
: setActiveB((activeB) => !activeB)
},[])
...
<SettingToggle
action={{
content: contentStatusA,
onAction: () => handleToggle('A')
}}
enabled={activeA}
>
它的功能,但感觉有点幼稚。对于更多 React-ish,reducer 可能是可行的方法。
带减速机
这看起来更简洁,如果您需要更多开关,它肯定更可扩展。
function reducer(state, action) {
switch (action.type) {
case "toggleA":
const newValueA = !state.activeA;
return {
...state,
activeA: newValueA,
contentStatusA: newValueA ? "Deactivate" : "Activate",
textStatusA: newValueA ? "activated" : "deactivated"
};
case "toggleB":
const newValueB = !state.activeB;
return {
...state,
activeB: newValueB,
contentStatusB: newValueB ? "Deactivate" : "Activate",
textStatusB: newValueB ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
activeA: false,
activeB: false,
contentStatusA: "Activate",
contentStatusB: "Activate",
textStatusA: "deactivated",
textStatusB: "deactivated"
};
export default function SettingToggleExample() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<>
<SettingToggle
action={{
content: state.contentStatusA,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeA}
>
This setting is <TextStyle variation="strong">{state.textStatusA}</TextStyle>.
</SettingToggle>
<SettingToggle
action={{
content: state.contentStatusB,
onAction: () => dispatch({type: 'toggleA'})
}}
enabled={state.activeB}
>
This setting is <TextStyle variation="strong">{state.textStatusB}</TextStyle>.
</SettingToggle>
</>
);
}
带有包装组件
子组件可以消除 'A' 和 'B' 后缀
function reducer(state, action) {
switch (action.type) {
case "toggle":
const newValue = !state.active;
return {
...state,
active: newValue,
contentStatus: newValue ? "Deactivate" : "Activate",
textStatus: newValue ? "activated" : "deactivated"
};
default:
throw new Error();
}
}
const initialState = {
active: false,
contentStatus: "Activate",
textStatus: "deactivated",
};
const ToggleWrapper = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<SettingToggle
action={{
content: state.contentStatus,
onAction: () => dispatch({ type: "toggle" })
}}
enabled={state.active}
>
This setting is <TextStyle variation="strong">{state.textStatus}</TextStyle>.
</SettingToggle>
)
}
export default function SettingToggleExample() {
return (
<>
<ToggleWrapper />
<ToggleWrapper />
</>
);
}
对每个切换使用简单的布尔值
如果您将活动状态对象合并到一个数组中,那么您可以动态更新任意数量的设置。这是一个可能看起来像的例子:
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
export default function SettingToggleExample() {
// define stateful array of size equal to number of toggles
const [active, setActive] = useState(Array(2).fill(false));
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive(prev => [...prev.slice(0,i), !prev[i], ...prev.slice(i+1)])
}, []);
return (
<>
{activeStatuses.map((isActive, index) =>
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
>
This setting is <TextStyle variation="strong">{isActive ? "activated" : "deactivated"}</TextStyle>.
</SettingToggle>
}
</>
);
}
当然,您以后可能希望为其中的每一个添加一个标签,因此最好在函数范围之外定义一个 defaultState 对象并用它替换 Array(2).fill(false)
。然后,除了布尔值 active
属性 之外,您还可以为每个切换设置一个字符串 label
属性,可以将其添加到 .map(...)
中每个切换的旁边。
为每个切换添加标签
根据您的跟进,这里是在 CodeSandbox 中也找到的实现,用于每个切换带有标签的状态(包括此处关于防止 link 衰减的答案):
import React, { useCallback, useState } from "react";
import { SettingToggle, TextStyle } from "@shopify/polaris";
const defaultState = [
{
isActive: false,
label: "A"
},
{
isActive: false,
label: "B"
},
{
isActive: false,
label: "C"
}
];
export default function SettingToggleExample() {
const [active, setActive] = useState(defaultState);
const handleToggle = useCallback((i) => {
// toggle the boolean at index, i
setActive((prev) => [
...prev.slice(0, i),
{ ...prev[i], isActive: !prev[i].isActive },
...prev.slice(i + 1)
]);
}, []);
return (
<div style={{ height: "100vh" }}>
{active?.map(({ isActive, label }, index) => (
<SettingToggle
action={{
content: isActive ? "Deactivate" : "Activate",
onAction: () => handleToggle(index)
}}
enabled={isActive}
key={index}
>
This {label} is
<TextStyle variation="strong">
{isActive ? "activated" : "deactivated"}
</TextStyle>
.
</SettingToggle>
))}
</div>
);
}