1

我使用 Shopify Polaris 的设置切换。https://polaris.shopify.com/components/actions/setting-toggle#navigation

而且我想实现的不仅是一个,而且是多个设置切换。但我不想总是重复相同的 handleToggle() 和 values(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

所以我想我可以使用自定义钩子。但它不起作用。所以如果你给我一些建议会很有帮助。

4

2 回答 2

1

为每个切换使用简单的布尔值

如果将活动状态对象组合到一个数组中,则可以动态更新任意数量的设置。这是一个可能看起来像的示例:

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)。然后label,除了active可以在.map(...).

为每个切换添加标签

根据您的跟进,这里也是在CodeSandbox中找到的带有每个切换标签的状态的实现(包括这里的答案以防止链接衰减):

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>
  );
}
于 2022-03-03T03:20:55.460 回答
1

我的第一次重构尝试是在公共处理程序上使用一个参数

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 />
    </>
  );
}
于 2022-03-03T03:15:33.930 回答