React hooks:如何观察 JS 类对象的变化?

Posted

技术标签:

【中文标题】React hooks:如何观察 JS 类对象的变化?【英文标题】:React hooks : how to watch changes in a JS class object? 【发布时间】:2021-02-10 04:17:03 【问题描述】:

我对 React 很陌生,我并不总是明白什么时候必须使用钩子,什么时候不需要它们。

我的理解是你可以通过使用来获取/设置状态

const [myState, setMyState] = React.useState(myStateValue);

所以。我的组件基于 url 属性运行一些功能:

const playlist = new PlaylistObj();

React.useEffect(() => 
  playlist.loadUrl(props.url).then(function()
    console.log("LOADED!");
  )
, [props.url]);

在我的 PlaylistObj 类中,我有一个异步函数 loadUrl(url)

将播放列表的 apiLoading 属性设置为 true 获取内容 将播放列表的 apiLoading 属性设置为 false

现在,我想在我的 React 组件中使用该值,这样我就可以设置它的类(我使用的是classnames):

  <div
  className=classNames(
    'api-loading': playlist.apiLoading
  )
  >

但它不起作用;课程没有更新,即使我得到“加载!”控制台中的消息。 React 似乎没有“观看”播放列表对象。也许我应该在这里使用反应状态,但是如何?

我测试过

const [playlist, setPlaylist] = React.useState(new PlaylistObj());
React.useEffect(() => 
  //refresh playlist if its URL is updated
  playlist.loadUrl(props.playlistUrl).then(function()
    console.log("LOADED!");
  )
, [props.playlistUrl]);

这个,但对我来说似乎越来越不合逻辑,而且,好吧,行不通。

const [playlist, setPlaylist] = React.useState(new PlaylistObj());
React.useEffect(() => 
  playlist.loadUrl(props.playlistUrl).then(function()
    console.log("LOADED!");
    setPlaylist(playlist); //added this
  )
, [props.playlistUrl]);

我只想让我的组件与播放列表对象保持同步。我该如何处理?

我觉得我错过了什么。

非常感谢!

【问题讨论】:

【参考方案1】:

我认为你已经接近了,但基本上这个问题是你没有实际上更新状态引用来触发另一个具有正确加载值的重新渲染。

const [playlist, setPlaylist] = React.useState(new PlaylistObj());

React.useEffect(() => 
  playlist.loadUrl(props.playlistUrl).then(function()
    setPlaylist(playlist); // <-- this playlist reference doesn't change
  )
, [props.playlistUrl]);

我认为你应该为你的组件引入第二个isLoading 状态。当 URL 更新时触发效果时,首先将 loading 设置为 true,当 Promise 解析时将其更新回 false。

const [playlist] = React.useState(new PlaylistObj());
const [isloading, setIsLoading] = React.useState(false);

React.useEffect(() => 
  setIsLoading(true);
  playlist.loadUrl(props.playlistUrl).then(function()
    console.log("LOADED!");
    setIsLoading(false);
  );
, [props.playlistUrl]);

在渲染中使用isLoading 状态

<div
  className=classNames(
    'api-loading': isLoading,
  )
>

我还建议使用 Promise 链的 finally 块来结束加载,以防 Promise 被拒绝,您的 UI 不会陷入加载“状态”。

React.useEffect(() => 
  setIsLoading(true);
  playlist.loadUrl(props.playlistUrl)
    .then(function() 
      console.log("LOADED!");
    )
    .finally(() => setIsLoading(false));
, [props.playlistUrl]);

【讨论】:

【参考方案2】:

给你:

import React from "react";

class PlaylistAPI 
  constructor(data = []) 
    this.data = data;
    this.listeners = [];
  
  addListener(fn) 
    this.listeners.push(fn);
  

  removeEventListener(fn) 
    this.listeners = this.listeners.filter(prevFn => prevFn !== fn)
  

  setPlayList(data) 
    this.data = data;

    this.notif();
  

  loadUrl(url) 
    console.log("called loadUrl", url, this.data)
  

  notif() 
    this.listeners.forEach(fn => fn());
  
  


export default function App() 
  const API = React.useMemo(() => new PlaylistAPI(), []);

  React.useEffect(() => 
    API.addListener(loadPlaylist);

    /**
     * Update your playlist and when user job has done, listerners will be called 
     */
    setTimeout(() => 
      API.setPlayList([1,2,3])
    , 3000)

    return () => 
      API.removeEventListener(loadPlaylist);
    
  , [API])

  function loadPlaylist() 
    API.loadUrl("my url");
  

  return (
    <div className="App">
      <h1>Watching an object by React Hooks</h1>
    </div>
  );
  

Demo in Codesandbox

【讨论】:

好的,所以你在这里使用记忆。使用 this 而不是 React context 有什么好处?谢谢。 它通过接受一个返回值的函数然后仅在需要检索该值时调用该函数(这通常只会在每次依赖项数组中的元素在渲染)。

以上是关于React hooks:如何观察 JS 类对象的变化?的主要内容,如果未能解决你的问题,请参考以下文章

React的类组件与函数式组件的不同,以及与React Hooks的对比

如何渲染类型为对象数组的 React Hook 状态?

如何使用 React Hooks 将具有构造函数的类转换为功能组件?

如何更改数组状态容器中的对象属性? React Hooks 使用状态

如何写自定义 React Hook?

Apollo react hooks 数组对象突变