如何实现不仅是一种设置切换而且还有多种设置切换?

发布于 2025-01-11 06:19:37 字数 2238 浏览 0 评论 0 原文

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

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

所以我想我可以使用自定义钩子。但这不起作用。因此,如果您给我一些建议,将会很有帮助。

I use Shopify Polaris's setting toggle.https://polaris.shopify.com/components/actions/setting-toggle#navigation

And I want to implement not only one but multi setting toggles.But I don't want to always duplicate same handleToggle() and values(contentStatus, textStatus) like below the sandbox 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

So I thought I can use a custom hook. But it's not working. So it would be helpful if you give me some advice.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笑,眼淚并存 2025-01-18 06:19:37

我第一次尝试重构时会在公共处理程序 It 函数上使用参数

const handleToggle = useCallback((which) => {
  which === 'A' ? setActiveA((activeA) => !activeA) 
   : setActiveB((activeB) => !activeB)
},[])

...

<SettingToggle
  action={{
    content: contentStatusA,
    onAction: () => handleToggle('A')
  }}
  enabled={activeA}
>

,但感觉有点天真。对于更多 React 风格的东西,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 />
    </>
  );
}

My first attempt to refactor would use a parameter on the common handler

const handleToggle = useCallback((which) => {
  which === 'A' ? setActiveA((activeA) => !activeA) 
   : setActiveB((activeB) => !activeB)
},[])

...

<SettingToggle
  action={{
    content: contentStatusA,
    onAction: () => handleToggle('A')
  }}
  enabled={activeA}
>

It functions, but feels a bit naïve. For something more React-ish, a reducer might be the way to go.


With a reducer

This seems cleaner, and is definitely more extensible if you need more toggles.

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>
    </>
  );
}

With a wrapper component

A child component can eliminate the 'A' and 'B' suffixes

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 />
    </>
  );
}
人疚 2025-01-18 06:19:37

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

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

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) 用它。然后,除了可以添加到 .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>
  );
}

Using simple Booleans for each toggle

If you combine your active state objects into a single array, then you can update as many settings as you would like dynamically. Here's an example of what that might look like:

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>
      }
    </>
  );
}

Of course, you will likely want to add a label to each of these going forward, so it may be better to define a defaultState object outside the function scope and replace the Array(2).fill(false) with it. Then you can have a string label property for each toggle in addition to a boolean active property which can be added next to each toggle in the .map(...).

With labels added for each toggle

Per your follow up, here is the implementation also found in the CodeSandbox for a state with labels for each toggle (including here on the answer to protect against link decay):

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>
  );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文