【问题标题】:creating a Boolean flag for onclick method为 onclick 方法创建一个布尔标志
【发布时间】:2020-11-07 21:21:45
【问题描述】:

我遇到了一些可能非常简单的 OOP 问题。我有一个呈现一些 html 的类。但是,它有一个 onClick 调用一个函数,如果单击图像,该函数会在类内设置一个标志。现在问题来了,当我渲染这个类对象并从一个单独的 js 文件中单击按钮时,它保持为假。我希望它在单击时将标志永久更改为 true。这里是类...

class Settings extends React.Component {

    handleClick() {
        this.flag = true;
        console.log(this.flag)
      }
  
    render(){
        return(
            <img src="./img/leaf.png" alt="" onClick={() => this.handleClick()}/>
        );   
    }

}

这是从单独文件中调用它的代码...

const settingsObj = new Settings();
console.log(settingsObj.flag);

我希望标志为假,直到按钮被点击,然后它永久更改为真。但它只有在我的页面随着新数据的进入而重新呈现并且它重置为 false 时才会成立。我尝试了构造函数和其他一些技术,但没有成功。

【问题讨论】:

  • 使用本地存储保存数据,否则每次刷新都会为假developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
  • 如果你想永久保存一些东西,你需要在服务器或本地存储中存储一些东西。如果您希望某些内容仅适用于当前会话,则可能需要查看 Context
  • 你为什么要显式调用const settingsObj = new Settings(); ?通常,组件在 React 中是相互添加的,这是确保状态保持不变的方法,直到您更改它们的值。如果你有 Component App,可以考虑在 App 的 render 函数中添加 Settings Component,有点像 render() { return (&lt;Settings /&gt;); } 。 React 会为你做正确的 OOP 调用
  • 我绝对想要后者,上下文。有任何链接或解决方案吗?
  • KeitelDOG 你能举个例子吗?如果我移除对象但它不持有,我就是这样做的。

标签: javascript reactjs class oop object


【解决方案1】:

普通的 OOP 设计原则并不总是直接适用于 React 组件。组件通常没有实例属性,它们大多只有 propsstate (有一些例外情况你确实使用实例属性,例如 react-native 中的 Animation 对象,但这些很少见)。

您以一种在这里不太合理的方式混合了这两种东西。 Settings 是一个渲染图像的 React 组件,但它也是一个对象,您可以通过调用 new Settings() 来实例化它。如果有其他组件依赖于flag 的值,您可能希望将标志的访问和存储与渲染组件分开,将值和回调传递给渲染器。

const Settings = ({setFlag}) => {
      return(
          <img src="./img/leaf.png" alt="" onClick={() => setFlag(true)}/>
      );
}

您建议您喜欢Context API 作为使flag 值在全球范围内可用的解决方案。有几种设置方法,但这里有一种。

在任何组件之外,我们创建一个FlagContext 对象,它有两个属性:booleanflag 和回调函数setFlag。我们需要给它一个默认的回退值,希望它永远不会被使用,所以我们的默认回调只是记录一个警告并且什么都不做。

const FlagContext = createContext<FlagContextState>({
  flag: false,
  setFlag: () => console.warn("attempted to use FlagContext outside of a valid provider")
});

这个FlagContext 对象放弃了ProviderConsumer 组件,但我们可以将value 提供给FlagContext.Provider。因此,我们将创建一个处理该部分的自定义组件。我们自定义的FlagProvider 使用本地状态来创建和传递值。我用过函数组件,但你也可以用类组件。

const FlagProvider = ({children}) => {
  const [flag, setFlag] = useState(false);

  return (
    <FlagContext.Provider value={{
      flag,
      setFlag
    }}>
      {children}
    </FlagContext.Provider>
  )
}

我们想把整个应用程序放在FlagProvider里面,这样整个应用程序就有可能访问flagsetFlag,并且整个应用程序得到相同的@987654346 @值。

当您想在组件中使用上下文中的值时,您可以使用useContext 挂钩或Consumer 组件。无论哪种方式,我都喜欢创建一个别名并将其导出,而不是直接导出 FlagContext 对象。

export const FlagConsumer = FlagContext.Consumer;

export const useFlagContext = () => useContext(FlagContext);

对于Consumer,消费者的孩子是一个函数,它接受上下文的值,在这种情况下,它是一个具有属性flagsetFlag的对象,并返回一些JSX。

这通常是你定义的内联函数:

const SomePage = () => {
  return (
    <FlagConsumer>
      {({flag, setFlag}) => (<div>Flag Value is {flag.toString()}</div>)}
    </FlagConsumer>
  )
}

但它也可以是一个函数组件。请注意,当使用函数组件作为子组件时,您必须传递组件本身 ({Settings}) 而不是它的执行版本 (&lt;Settings /&gt;)。

const Settings = ({ setFlag }) => {
  return <img src="./img/leaf.png" alt="" onClick={() => setFlag(true)} />;
};

const SomePage = () => {
  return <FlagConsumer>{Settings}</FlagConsumer>;
};

现在首选的方法是使用钩子。我们在函数体内部调用useFlagContext(),它返回我们的上下文对象。

const SomePage = () => {
  const {flag, setFlag} = useFlagContext();

  return <Settings setFlag={setFlag}/>
};

consumer 和 hook 只有在 flag context provider 中才起作用,所以这就是我们将它放在整个应用程序中的原因!

const App = () => {
  return (
    <FlagProvider>
      <SomePage />
    </FlagProvider>
  );
};

CodeSandbox上的完整示例

【讨论】:

    【解决方案2】:

    对于此类交互,我强烈建议您使用 Redux

    我确信您会从中受益的另一个想法是切换到 hooks函数组件:样板更少,代码更灵活。

    回到目标,使用 Redux,您的代码看起来类似于:

    const Settings = (props) => {
      const dispatch = useDispatch();
      const flag = useSelector(state => state.yourStoreObj.flag);
          
      handleClick() {
        dispatch(yourCustomAction("UPDATE_FLAG", true));
      }
      
      return(
            <img src="./img/leaf.png" alt="" onClick={() => handleClick()}/>
        );  
    }
    

    说明

    首先,花 15 分钟熟悉 React Redux。这是一个good practical article 开始。如果您不熟悉钩子,请start learning them,因为它会发生很大变化,而您不需要更改到目前为止所做的任何一行。

    我们假设商店中有一个属性是该特定元素的“标志”属性。通过这种方式,属性可以由组件本身通过 useSelector() 操作符读取,或者可以在应用程序中的任何位置以相同的方法从任何其他组件读取.

    同样,您可以通过调度更改来更改值(请参阅 dispatch() 函数),同样,您可以从任何其他组件执行此操作。

    所以,假设您想在点击发生在完全不同的组件上时更改该属性,这可能是另一个组件的样子

    const OtherCoolComp = (props) => {
      const dispatch = useDispatch();
      
      handleClick() {
        dispatch(yourCustomAction("UPDATE_FLAG", true));
      }
      
      return(
            <button onClick={() => handleClick()}>
              Click me!
            </button>
        );  
    }
    

    因此,您从不知道谁在显示该值的组件中调度相同的操作,并将其设置为您喜欢的值。

    【讨论】:

    • 这很棒。我通过简化代码和使用带有钩子的函数来解决。我也会研究redux。谢谢!
    • 值得注意的是,Redux 在幕后使用了 React Context API。我喜欢 Redux。如果全局状态是单个布尔值,我不会推荐它,但是当有一个大而复杂的全局状态时,它会很棒。
    • @LindaPaiste 你是对的,但只有布尔值的 React 应用程序是我从未见过的。对于一个布尔值,Redux 肯定是一个很大的结构,但是一旦你设置了它,你就可以用它做很多其他的事情。也就是说,我也非常感谢您的回答,非常完整和详细!
    猜你喜欢
    • 2018-09-30
    • 1970-01-01
    • 2023-03-23
    • 2011-05-26
    • 1970-01-01
    • 2017-10-12
    • 2015-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多