如何在 React 经典 `class` 组件中使用 React 钩子?

Posted

技术标签:

【中文标题】如何在 React 经典 `class` 组件中使用 React 钩子?【英文标题】:How can I use React hooks in React classic `class` component? 【发布时间】:2019-04-21 14:52:03 【问题描述】:

在这个例子中,我有这个反应类:

class MyDiv extends React.component
   constructor()
      this.state=sampleState:'hello world'
   
   render()
      return <div>this.state.sampleState
   

问题是我是否可以为此添加 React 钩子。我知道 React-Hooks 是 React Class 风格的替代品。但是如果我想慢慢迁移到 React hooks 中,我可以在 Classes 中添加有用的 hooks 吗?

【问题讨论】:

【参考方案1】:

High order components 是我们一直在做这类事情的方式,直到出现钩子。你可以为你的钩子写一个简单的高阶组件包装器。

function withMyHook(Component) 
  return function WrappedComponent(props) 
    const myHookValue = useMyHook();
    return <Component ...props myHookValue=myHookValue />;
  

虽然这并不是真正直接使用类组件中的钩子,但这至少允许您使用类组件中的钩子的逻辑,而无需重构。

class MyComponent extends React.Component 
  render()
    const myHookValue = this.props.myHookValue;
    return <div>myHookValue</div>;
  


export default withMyHook(MyComponent);

【讨论】:

优秀的答案。我将这种方法用于使用 Hooks 编写的 react-alert 包。 直接给类(MyDiv)赋值可以吗?它给出了一个 es-lint 错误! @TusharShukla 从技术上讲,覆盖这样的类值是可以的——但通常有点不赞成,是的,eslint 想要在默认情况下停止它。更新了示例以使其更加清晰。 在第一个代码块中,返回行不应该是Component而不是MyComponent吗? @NicholasHamilton 不——这个例子是正确的。第一个代码块是第二个代码块中类组件的示例高阶组件包装器。在此示例中,高阶组件包装器使用 useMyHook() 挂钩,并将该挂钩的结果作为名为 myHookValue 的道具提供给类组件。【参考方案2】:

类组件不支持挂钩 -

根据Hooks-FAQ:

你不能在类组件中使用 Hooks,但你绝对可以将类和函数组件与 Hooks 混合在一个树中。组件是使用 Hooks 的类还是函数是该组件的实现细节。从长远来看,我们希望 Hooks 成为人们编写 React 组件的主要方式。

【讨论】:

但是请记住,我们绝对可以将类和函数组件混合在一个树中。 我一直在课堂上看到 React.createRef 并感到困惑,以为它是 React.useRef ?【参考方案3】:

钩子不是用于类,而是用于函数。如果你想使用钩子,你可以从编写新代码作为带有钩子的功能组件开始

根据React FAQs

你不能在类组件中使用 Hooks,但是你可以 绝对将类和函数组件与 Hooks 混合在一起 树。组件是使用 Hooks 的类还是函数 该组件的实现细节。从长远来看,我们 希望 Hooks 成为人们编写 React 组件的主要方式。

const MyDiv = () => 
   const [sampleState, setState] = useState('hello world');
   render()
      return <div>sampleState</div>
   

【讨论】:

应该是setSampleState 不? 它是一个简单的数组解构,可以命名任何东西。 setState, setSampleState, setABC【参考方案4】:

正如其他答案已经解释的那样,钩子 API 旨在为功能组件提供当前仅在类组件中可用的功能。钩子不应该在类组件中使用。

可以编写类组件以更轻松地迁移到函数组件。

单一状态:

class MyDiv extends Component 
   state = sampleState: 'hello world';

   render()
      const  state  = this;
      const setState = state => this.setState(state);

      return <div onClick=() => setState(sampleState: 1)>state.sampleState</div>;
   

转换为

const MyDiv = () => 
   const [state, setState] = useState(sampleState: 'hello world');

   return <div onClick=() => setState(sampleState: 1)>state.sampleState</div>;

注意useState 状态设置器不会自动合并状态属性,这应该用setState(prevState =&gt; ( ...prevState, foo: 1 )) 覆盖;

具有多种状态:

class MyDiv extends Component 
   state = sampleState: 'hello world';

   render()
      const  sampleState  = this.state;
      const setSampleState = sampleState => this.setState( sampleState );

      return <div onClick=() => setSampleState(1)>sampleState</div>;
   

转换为

const MyDiv = () => 
   const [sampleState, setSampleState] = useState('hello world');

   return <div onClick=() => setSampleState(1)>sampleState</div>;

【讨论】:

【参考方案5】:

补充Joel Cox's good answer


Render Props 还允许在类组件中使用 Hook,如果需要更大的灵活性:

class MyDiv extends React.Component 
  render() 
    return (
      <HookWrapper
        // pass state/props from inside of MyDiv to Hook
        someProp=42 
        // process Hook return value
        render=hookValue => <div>Hello World! hookValue</div> 
      />
    );
  


function HookWrapper( someProp, render ) 
  const hookValue = useCustomHook(someProp);
  return render(hookValue);

对于没有返回值的副作用Hooks:

function HookWrapper( someProp ) 
  useCustomHook(someProp);
  return null;


// ... usage
<HookWrapper someProp=42 />

来源:React Training

【讨论】:

【参考方案6】:

React Hooks 让您无需编写类即可使用反应特性和生命周期。 它就像类组件的等效版本,具有更小和可读的外形。您应该迁移到 React 钩子,因为编写它很有趣。 但是你不能在类组件中编写反应钩子,因为它是为功能组件引入的。

这可以很容易地转换为:

class MyDiv extends React.component
   constructor()
      this.state=sampleState:'hello world'
   
   render()
      return <div>this.state.sampleState
   


const MyDiv = () => 
   const [sampleState, setSampleState] = useState('hello world');
   return <div>sampleState</div>

【讨论】:

【参考方案7】:

您可以使用react-universal-hooks 库。它允许您在类组件的渲染函数中使用“useXXX”函数。

到目前为止,它对我来说效果很好。唯一的问题是因为它没有使用官方的钩子,所以值不显示 react-devtools。

为了解决这个问题,我通过包装钩子创建了一个等效项,并让它们将数据存储在component.state.hookValues 上(使用对象变异来防止重新渲染)。 (你可以通过自动包装组件render函数来访问组件,运行set currentCompBeingRendered = this

有关此问题的更多信息(以及解决方法的详细信息),请参阅此处:https://github.com/salvoravida/react-universal-hooks/issues/7

【讨论】:

【参考方案8】:

您可以通过通用High order components 实现此目的

HOC

import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => 
  return function WrappedComponent(props) 
    const hookValue = useHook();
    return <Component ...props ...[hookName]: hookValue />;
  ;
;

export default withHook;

用法

class MyComponent extends React.Component 
      render()
        const myUseHookValue = this.props.myUseHookValue;
        return <div>myUseHookValue</div>;
      
    

export default withHook(MyComponent, useHook, 'myUseHookValue');

【讨论】:

【参考方案9】:

有状态组件或容器或基于类的组件曾经支持 React Hooks 的功能,因此我们不需要在有状态组件中仅在无状态组件中 React Hooks。

一些附加信息

什么是 React Hooks? 那么什么是钩子呢? Well hooks 是一种新方式,或者为我们提供了一种编写组件的新方式。

到目前为止,我们当然有函数式和基于类的组件,对吧?功能组件接收 props,然后返回一些应该呈现到屏幕上的 JSX 代码。

它们非常适合演示,因此用于呈现 UI 部分,而不是业务逻辑,它们通常专注于每个组件的一个或几个目的。

另一方面,基于类的组件也将接收道具,但它们也具有这种内部状态。因此,基于类的组件是实际持有我们大部分业务逻辑的组件,所以对于业务逻辑,我的意思是我们发出 HTTP 请求,我们需要处理响应并更改应用程序的内部状态,或者可能即使没有 HTTP。用户填写表单,我们想在屏幕上的某个地方显示它,我们需要状态,我们需要基于类的组件,因此我们通常也使用基于类的组件来编排我们的其他组件并传递我们的状态例如,作为功能组件的道具。

现在我们在这种分离中遇到了一个问题,它增加了所有好处,但我们遇到的一个问题是从一种组件形式转换为另一种形式很烦人。这不是很困难,但很烦人。

如果您发现自己需要将功能组件转换为基于类的组件,则需要大量输入并且总是输入相同的内容,所以这很烦人。

引号中一个更大的问题是生命周期钩子很难正确使用。

显然,添加 componentDidMount 并在其中执行一些代码并不难,但知道要使用哪个生命周期钩子、何时以及如何正确使用它,这可能具有挑战性,尤其是在更复杂的应用程序中,无论如何,不​​是吗?如果我们有一种创建组件的方法,并且该超级组件可以同时处理状态和副作用(如 HTTP 请求)并呈现用户界面,那就太好了?

嗯,这正是钩子的全部意义所在。 Hooks 为我们提供了一种创建功能组件的新方法,这很重要。

【讨论】:

编写类组件不是问题。它们可以快速编写和重构。钩子的真正目的不是简化输入,而是让不同组件之间更容易共享逻辑。再注意一点:如果你不知道使用哪种生命周期方法,那么你也会用 Hooks 自己动手。【参考方案10】:

你现有的类组件是不可能的。您必须将您的类组件转换为功能组件,然后执行以下操作 -

function MyDiv() 
const [sampleState, setSampleState] = useState('hello world');
return (
      <div>sampleState</div>
    )

【讨论】:

【参考方案11】:

对我来说 React.createRef() 很有帮助。

例如:

constructor(props) 
      super(props);
      this.myRef = React.createRef();
   

...


<FunctionComponent ref=this.myRef />

原帖here.

【讨论】:

【参考方案12】:

是的,但不是直接的。

试试 react-iifc,更多细节在它的自述文件中。

https://github.com/EnixCoda/react-iifc

【讨论】:

【参考方案13】:
    尝试使用组件挂钩: https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';


class MyComponent extends React.Component 

    render()
        const props = this.props;
        const [counter, set] = React.useState(0);

        //TODO...

    


export default withComponentHooks(MyComponent)

2.试试react-iifc:https://github.com/EnixCoda/react-iifc

【讨论】:

以上是关于如何在 React 经典 `class` 组件中使用 React 钩子?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 React JS 在 Material UI 中使整个 Card 组件可点击?

react-hook-form 在组件中使用以传递动态文本字段的道具

如何在 React.Js 内部渲染中使链接可点击?

如何在 React Native 中使 Tab.Navigation Screens 默认背景为白色?

如何在 React Native 中使 2 个 <Text> 内联

如何在 react-native 中使文本的某些部分可点击并且某些部分具有不同的文本颜色