zoukankan      html  css  js  c++  java
  • React函数式组件和类组件[Dan]

    一篇对DanHow Are Function Components Different from Classes? 一文的个人阅读总结,内容来自于此。强烈推荐阅读 Dan Abramov.的博客

    函数式组件和Class组件有什么不同?

    Dan很直接的给出了答案:

    函数式组件捕获了渲染所用的值。(Function components capture the rendered values.)

    直接看结论可能有点不知所云。

    class组件可能引发的"错误"


    看一个组件,使用setTimeout模拟网络请求,点击button之后警告提示关注某人(user),userprops中读取。

    该组件的function版本:

    function ProfilePage(props) {
      const showMessage = () => {
        alert('Followed ' + props.user);
      };
    
      const handleClick = () => {
        setTimeout(showMessage, 3000);
      };
    
      return (
        <button onClick={handleClick}>Follow</button>
      );
    }
    

    class版本:

     class ProfilePage extends React.Component {
      showMessage = () => {
        alert('Followed ' + this.props.user);
      };
    
      handleClick = () => {
        setTimeout(this.showMessage, 3000);
      };
    
      render() {
        return <button onClick={this.handleClick}>Follow</button>;
      }
    }
    

    页面组件代码:

    class App extends React.Component {
      state = {
        user: 'Dan',
      };
      render() {
        return (
          <>
            <label>
              <b>Choose profile to view: </b>
              <select
                value={this.state.user}
                onChange={e => this.setState({ user: e.target.value })}
              >
                <option value="Dan">Dan</option>
                <option value="Sophie">Sophie</option>
                <option value="Sunil">Sunil</option>
              </select>
            </label>
            <h1>Welcome to {this.state.user}’s profile!</h1>
            <p>
              <ProfilePageFunction user={this.state.user} />
              <b> (function)</b>
            </p>
            <p>
              <ProfilePageClass user={this.state.user} />
              <b> (class)</b>
            </p>
            <p>
              Can you spot the difference in the behavior?
            </p>
          </>
        )
      }
    }
    

    对于class组件,在选中状态是userA的时候,点击follow button之后立马将select切换其他人(uerB),三秒之后的弹出框是followuserB。(这个动作会在后面多次提及)

    在选中userA的时候点击关注,目的就是关注userA,但是class组件最后弹出框显示的关注userB,这显然不符合预期。

    为什么、如何解决


    如果上面例子使用function组件,弹出框显示的就会是正确的,虽然切换到了选中的userB,但是弹出框显示的仍然是点击的那一刻关注的userA

    function组件好像记下了点击那一刻时候的状态?

    class组件在这个场景中错误的原因是class组件每次三秒后从this.props.user中读取数据,此时的this.props.user已经变了,已经是切换后的新的this.props.user数据。虽然React中props不可变,但是this是可变的

    类组件会随着时间推移改变,在渲染方法和生命周期方法中得到的是最新的实例。而函数式组件的事件处理程序就是渲染结果的一部分,事件处理程序属于一个拥有特定的propsstate的渲染

    也就是说函数式组件保持了事件处理程序与那一次渲染propsstate之间的联系,本身就是正确的。


    来修复类组件中的这个问题:

    1. 可以在点击的时候就读取并记录当下的stateprops,三秒后读取记录的数据(而不是读this.props.xx)再弹出。

      方案可行但是扩展极差,在其他多个变量也这样做的时候逐层记录或传递非常繁复。

    2. 闭包。

      闭包维持了一个可能随时间变化的变量,而此处我们要维持的是React的propsstate,React设计中这都是不可变的。让闭包来维持不变的stateprops,此时再去捕获这些值,就是一致的。

      render函数中使用闭包:

      class ProfilePage extends React.Component {
        render() {
          // Capture the props!
          const props = this.props;
      
          // Note: we are *inside render*.
          // These aren't class methods.
          const showMessage = () => {
            alert('Followed ' + props.user);
          };
      
          const handleClick = () => {
            setTimeout(showMessage, 3000);
          };
      
          return <button onClick={handleClick}>Follow</button>;
        }
      }
      

      渲染的时候这些需要使用的props已经被捕获(就像上面方案1的记录,在render的时候就已经读取记录下了)。此时表现弹出内容就会是点击时候的那个userA了。


    class组件的这个问题是修复了,但是在render函数中添加那么多的函数,且并没有挂载到class上,有点奇怪?

    其实去掉class,这就是函数式组件的形式了:

    function ProfilePage(props) {
      const showMessage = () => {
        alert('Followed ' + props.user);
      };
    
      const handleClick = () => {
        setTimeout(showMessage, 3000);
      };
    
      return (
        <button onClick={handleClick}>Follow</button>
      );
    }
    

    React将他们作为参数传递,props在渲染时被捕获了。不同于class组件的this,这里的props不会被改变。

    点击事件处理函数,该函数属于具有正确user值的一次渲染,事件处理函数和其他回调函数也能读到这个值。

    回头看这个结论,是不是更好理解一点了:

    函数式组件捕获了渲染所使用的值

    函数式组件使用最新的propsstate


    函数式组件捕获了特定渲染的propsstate。但是我们如果又想和class组件一样读取最新的propsstate呢?

    useRef

    Dan 老师:在函数式组件中,你也可以拥有一个在所有的组件渲染帧中共享的可变变量。它被成为“ref”

    this.something就像是something.current的一个镜像。他们代表了同样的概念。

    每一次的渲染结果可以视为一个渲染帧,共享的变量设置为ref,包含DOMRefclass中的实例变量的功能,可以说是非常强大了。

    需要最新的propsstate值,可以使用useRef创建的变量来记录,通过useEffect可以在值变化的时候自动追踪。

    function MessageThread() {
      const [message, setMessage] = useState('');
    
      // 保持追踪最新的值。
      const latestMessage = useRef('');
      useEffect(() => {
        latestMessage.current = message;
      });
    
      const showMessage = () => {
        alert('You said: ' + latestMessage.current);
      };
    

    React函数总是捕获他们的值

  • 相关阅读:
    Oracle数据库部分迁至闪存存储方案
    RAC环境下误操作将数据文件添加到本地存储
    Oracle的窗口和自动任务
    ####### Scripts Summary #######
    plsql 操纵表数据的2种方式
    css 如何使图片与文字在div中居中展示?
    eclipse svn新增文件不显示在文件列表,只有修改文件可以提交!
    js 正则表达式校验必须包含字母、数字、特殊字符
    css 禁止录入中文
    POJ 1740:A New Stone Game
  • 原文地址:https://www.cnblogs.com/xuxiaowei/p/14485289.html
Copyright © 2011-2022 走看看