【问题标题】:How to specify a constructor with a functional component (fat arrow syntax)?如何指定具有功能组件的构造函数(粗箭头语法)?
【发布时间】:2017-05-30 13:39:40
【问题描述】:

给定这个组件:

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const NewGoalInput = props => {
  return (
    <input type="text" onKeyUp={handleKeyUp}/>
  )
}

const handleKeyUp = (e) => {
  if (e.key === "Enter") {
    // TODO Add goal
  }
}

export default NewGoalInput

如何在不使用extends React.Component 语法的情况下添加可以定义状态的构造函数?

【问题讨论】:

  • 功能组件没有实现生命周期工作流,尽管它们没有状态。如果你想使用状态,你必须从React.Component扩展

标签: reactjs ecmascript-6


【解决方案1】:

由于它是一个无状态组件,它没有组件生命周期。 因此您不能指定constructor

您必须扩展 React.Component 以创建一个有状态组件,然后该组件需要一个构造函数,并且您将能够使用 state

更新 自从React 16.8.0 和 Hooks 被引入以来,有了更多的选择。

Hooks 是一个新的特性提议,它允许你在不编写类的情况下使用状态和其他 React > 特性。它们作为 > v16.8.0

的一部分在 React 中发布

无状态:

import React from "react"

const Stateless = ({name}) => (
  <div>{`Hi ${name}`}</div>
);

有状态:

可以访问组件生命周期方法和本地状态。

class Stateful extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    const { count } = this.state;
    document.title = `You've clicked ${count} times.`;
  }

  componentDidUpdate() {
    const { count } = this.state;
    document.title = `You've clicked ${count} times.`;
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <p>You've clicked {count} times.</p>
        <button onClick={() => this.setState({ count: count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

使用 Hooks:

能够使用State HookEffect Hook

如果你熟悉 React 类生命周期方法,可以将 useEffect Hook 视为 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合。

import React, { useState, useEffect } from "react";

const UsingHooks = () => {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You've clicked ${count} times.`;
  });

  return (
    // <> is a short syntax for <React.Fragment> and can be used instead of a wrapping div
    <>
      <p>You've clicked {count} times.</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </>
  );
}

【讨论】:

  • 很棒的解释。但是,这并不能完全回答问题,因为 constructor 并不是生命周期方法的一部分,这意味着 useEffect 钩子对在功能组件中创建 constructor 没有帮助。
【解决方案2】:

现在我们有了useState 和钩子,答案有点过时了。我遇到了这个问题,因为我做错了什么。这是我正在做的一些简化代码。

// set an initial state
const [ value, setValue ] = useState(0)

// gets called after component is re-rendered
useEffect(() => {
   // callback to parent that set props
   props.update()
})

// if we have an existing value passed in
if (props.value) {
   setValue(props.value)
}

这段代码使用钩子从有状态类转换为函数,最初在构造函数中设置默认道具 - 但函数没有构造函数,并且每次重新渲染组件时都会进行检查:

  1. 致电useState
  2. 触发重新渲染
  3. useEffect 被触发
  4. 调用 parent 来设置 props
  5. 道具更新,因此子级再次渲染
  6. 转到 1

如您所见,这会导致无限循环。解决方案非常简单。这是与原始版本的模拟差异。

- const [ value, setValue ] = useState(0)
+ const [ value, setValue ] = useState(props.value || 0)

- if (props.value) {
-   setValue(props.value)
- }

基本上,只需从 props 初始化状态,不要做诸如调用 useState 之类的傻事,除非响应某种类型的事件或回调。

【讨论】:

    【解决方案3】:

    您可以将 useState 设置为功能组件内的第一行,并添加一个函数作为“初始值”:

    const MyComponentName = props => {
      useState(() => {
        console.log('this will run the first time the component renders!');
      });
      return <div>my component!</div>;
    };
    

    【讨论】:

    • 如果你需要将任何值从你的“构造函数”暴露给组件,记住这个函数的返回是useState返回的数组中的第一项,这样:const [value] = useState(() =&gt; { /* ... */ return '123'; })
    【解决方案4】:

    你没有。您示例中的组件类型称为“无状态功能组件”。它没有状态,也没有生命周期方法。如果你希望你的组件是有状态的,你必须把它写成一个类组件。

    【讨论】:

    • React 的现代版本能够使用 hooks 在功能组件中管理 state,这会使您的答案过时。
    【解决方案5】:

    在 FC 中使用 useEffect 模拟构造函数。

    useEffect(() => {
      ... here your init code
    }, []);
    

    就是这样!易!这个 useEffect 只在组件加载时运行一次,之后永远不会运行,只是不要忘记在末尾添加方括号。

    【讨论】:

    • 谢谢!学到了一些东西。
    • useEffect 挂钩在渲染函数之后运行,因此即使您传递第二个参数(“方括号”),它们也不会提供与构造函数相同的功能。类组件中的构造函数在第一次渲染调用之前运行
    • 蒂莫菲是对的。 useEffect() 确实模仿了 componentDidMount - 不是构造函数。您不能使用它来设置应该在之后呈现的初始数据,就像在基于类的构造函数中完成的那样。
    • 它不是构造函数的替换,它是替换 componenetDidMount
    【解决方案6】:

    您可以使用useMemo 钩子(如下)作为功能组件的构造函数进行演示。有人建议使用useEffect但是它会在渲染后被调用。

    useMemo(() => {
      console.log('This is useMemo')
    }, []);
    

    【讨论】:

      【解决方案7】:

      对于那些想要在组件安装之前运行函数的人,这里有一个钩子(用 TypeScript 编写)。

      通常useEffectuseLayoutEffect 就足够了,但它们在组件安装后运行,有时您希望在此之前运行代码(如构造函数)。

      import React, { useRef } from "react";
      
      function useOnce<Type>(callBack: () => Type): Type {
        const result = useRef<Type | null>(null);
      
        if (result.current !== null) {
          return result.current;
        }
      
        result.current = callBack();
        return result.current;
      }
      
      const Component: React.FC<{}> = () => {
        const result = useOnce(() => {/* Code you would normally put in a constructor */});
      
        return <div />
      }
      

      【讨论】:

        猜你喜欢
        • 2021-03-15
        • 2019-06-17
        • 1970-01-01
        • 2021-09-26
        • 2018-08-24
        • 1970-01-01
        • 1970-01-01
        • 2021-02-09
        • 2021-07-04
        相关资源
        最近更新 更多