Reactjs React hook:如何在特定时间更新组件

Reactjs React hook:如何在特定时间更新组件,reactjs,react-hooks,Reactjs,React Hooks,我有一个具有expiredAt属性的模型: 可过期类型{ 到期日期:日期; } 组件思想基本上如下所示: const[model,setModel]=useState{ 过期日期:new Date new Date.getTime+20000 }; 回来 {过期模型? 期满 : 未到期 } ; 我如何实现过期的函数或在适当的时候更新组件 请注意,多个组件是使用从API获取的模型呈现的,因此硬编码在这里不是解决方案。Live demo 从React导入React,{useffect,useSta

我有一个具有expiredAt属性的模型:

可过期类型{ 到期日期:日期; } 组件思想基本上如下所示:

const[model,setModel]=useState{ 过期日期:new Date new Date.getTime+20000 }; 回来 {过期模型? 期满 : 未到期 } ; 我如何实现过期的函数或在适当的时候更新组件

请注意,多个组件是使用从API获取的模型呈现的,因此硬编码在这里不是解决方案。

Live demo

从React导入React,{useffect,useState}; 导入./styles.css; 导出默认功能应用程序{ const[model,setModel]=useState{ 过期日期:新日期。getTime+5000, 过期:假 }; const expired=m=>{ 返回m.expired===true; }; useEffect=>{ 设置超时=>{ setModelcurrentModel=>{…currentModel,过期:true}; },model.expiredAt-new Date.getTime; }, []; 返回{expiredmodel?Expired:notexpired}; } 现场演示

从React导入React,{useffect,useState}; 导入./styles.css; 导出默认功能应用程序{ const[model,setModel]=useState{ 过期日期:新日期。getTime+5000, 过期:假 }; const expired=m=>{ 返回m.expired===true; }; useEffect=>{ 设置超时=>{ setModelcurrentModel=>{…currentModel,过期:true}; },model.expiredAt-new Date.getTime; }, []; 返回{expiredmodel?Expired:notexpired}; }
您可以为模型添加过期属性,并在时间通信时将其设置为true

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
  expired: false
});
// You can also use useRef instead of useState here.
const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
     // setModel on currentModel to have last value of model
     setModel(currentModel => ({...currentModel, expired: true }))
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { model.expired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);
更新:如果你不想触摸模型,你可以这样做

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
});

const [isModelExpired, setIsModelExpired] = useState(false)

const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
    setIsModelExpired(true)
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { isModelExpired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);

您可以为模型添加过期属性,并在时间通信时将其设置为true

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
  expired: false
});
// You can also use useRef instead of useState here.
const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
     // setModel on currentModel to have last value of model
     setModel(currentModel => ({...currentModel, expired: true }))
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { model.expired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);
更新:如果你不想触摸模型,你可以这样做

const [model, setModel] = useState<Expirable>({
  expiredAt: new Date((new Date).getTime() + 20000)
});

const [isModelExpired, setIsModelExpired] = useState(false)

const [expirationTimeoutId] = useState(() =>{
  return setTimeout(() =>{
    setIsModelExpired(true)
  }, model.expiredAt - Date.now())
});
// You should clear timeout when the component is unmounted
useEffect(() =>{
   return () => clearTimeout(expirationTimeoutId)
},[])

return (
  { isModelExpired ? (
     <Text>Expired</Text>
  ) : (
     <Text>Not Expired</Text>
  )}
);
除了你说的

请注意,多个组件是使用从API获取的模型呈现的,因此硬编码在这里不是解决方案

但在您的示例中,您使用了硬编码的过期时间,而不是向我们展示一旦从API获取模型,如何将其提供给组件;我将假设模型作为道具提供给组件

曾经说过,React钩子的目的是使代码更短,更好地维护,但只有当组件必须做一些事情时,这才是正确的。这是一个典型的例子,使用类组件可以使代码更加简短、干净和可维护

class ModelComponent extends Component {
  constructor(props) {
    super(props);

    const elapsed = props.model.expiredAt - new Date().getTime();

    this.state = { expired: elapsed <= 0 };
    if(! this.state.expired) // Set the timeout only if the model isn't already expired
      this.state.to = setTimeout(() => this.setState({ expired: true, to: null }), elapsed);
  }

  componentWillUnmount() {
    if(this.state.to) clearTimeout(this.state.to);
  }

  render() {
    return this.state.expired ? (
      <Text>Expired</Text>
    ) : (
      <Text>Not Expired</Text>
    );
  }
}
这个代码是否比使用React钩子的可能解决方案更清晰或更复杂

希望这有帮助。

除了你说的

请注意,多个组件是使用从API获取的模型呈现的,因此硬编码在这里不是解决方案

但在您的示例中,您使用了硬编码的过期时间,而不是向我们展示一旦从API获取模型,如何将其提供给组件;我将假设模型作为道具提供给组件

曾经说过,React钩子的目的是使代码更短,更好地维护,但只有当组件必须做一些事情时,这才是正确的。这是一个典型的例子,使用类组件可以使代码更加简短、干净和可维护

class ModelComponent extends Component {
  constructor(props) {
    super(props);

    const elapsed = props.model.expiredAt - new Date().getTime();

    this.state = { expired: elapsed <= 0 };
    if(! this.state.expired) // Set the timeout only if the model isn't already expired
      this.state.to = setTimeout(() => this.setState({ expired: true, to: null }), elapsed);
  }

  componentWillUnmount() {
    if(this.state.to) clearTimeout(this.state.to);
  }

  render() {
    return this.state.expired ? (
      <Text>Expired</Text>
    ) : (
      <Text>Not Expired</Text>
    );
  }
}
这个代码是否比使用React钩子的可能解决方案更清晰或更复杂


希望这能有所帮助。

我建议您定义一个自定义挂钩,以简化组件代码和将来的重用:

import React, { useEffect, useState, useRef } from "react";
import "./styles.css";

const useExpired = (time)=>{
  const [expired, setExpired] = useState(false);
  const timoutRef = useRef();
  useEffect(()=>{
    timoutRef.current = setTimeout(()=>{
      setExpired(true);
    }, time);
    return ()=>{
      clearTimeout(timoutRef.current);
    }
  },[time]);
  return expired;
}

export default function App() {
  const [model, setModel] = useState({
    expiredAt: new Date().getTime() + 5000,
  });

  const expired = useExpired(model.expiredAt - new Date().getTime());

  return <div>{expired ? <h1>Expired</h1> : <h1>Not expired</h1>}</div>;
}

我建议您定义一个自定义挂钩,以简化组件代码和将来的重用:

import React, { useEffect, useState, useRef } from "react";
import "./styles.css";

const useExpired = (time)=>{
  const [expired, setExpired] = useState(false);
  const timoutRef = useRef();
  useEffect(()=>{
    timoutRef.current = setTimeout(()=>{
      setExpired(true);
    }, time);
    return ()=>{
      clearTimeout(timoutRef.current);
    }
  },[time]);
  return expired;
}

export default function App() {
  const [model, setModel] = useState({
    expiredAt: new Date().getTime() + 5000,
  });

  const expired = useExpired(model.expiredAt - new Date().getTime());

  return <div>{expired ? <h1>Expired</h1> : <h1>Not expired</h1>}</div>;
}

您没有正确使用useEffect。向第二个参数发送一个空数组。@bravemaster感谢您提出该参数,已更新。不知道为什么每次我添加空数组时,eslint都会给我一个警告。在codesandbox上也一样。使用//eslint禁用line react hooks/deps。因为setTimeout函数只需要调用一次,所以我们不需要模型依赖关系。欲了解更多信息,请参阅本文:像魅力一样工作!谢谢在CodeSandbox中更新setTimeout中直接访问模型可能会导致模型未更新问题:检查此处:您没有正确使用useEffect。向第二个参数发送一个空数组。@bravemaster感谢您提出该参数,已更新。不知道为什么每次我添加空数组时,eslint都会给我一个警告。在codesandbox上也一样。使用//eslint禁用line react hooks/deps。因为setTimeout函数只需要调用一次,所以我们不需要模型依赖关系。欲了解更多信息,请参阅本文:像魅力一样工作!谢谢在CodeSandbox中更新setTimeout中直接访问模型可能会导致模型未更新问题:检查此处:是否有任何方法不需要触摸模型属性?是@bravemaster您可以将过期的模型分离为新状态。检查我的更新答案。是否有任何方法不需要触摸模型属性?是@bravemaster,您可以将过期的模型分离为新状态。检查我的更新答案。模型从
我是API,就这件事来说也是。我只是想展示一个例子,我的代码库完全是React-hooks。如果我想用你的答案,有没有办法把React钩子和React组件结合起来?或者将React钩子与React类组件混合是一种很好的方法吗?混合它们绝对没有问题;我通常将主要组件作为类组件编写,因为它们功能更强大,而小型附属组件作为功能组件。。。在需要的时候使用钩子。模型是从API获取的,在这方面也是expiredAt。我只是想展示一个例子,我的代码库完全是React-hooks。如果我想用你的答案,有没有办法把React钩子和React组件结合起来?或者将React钩子与React类组件混合是一种很好的方法吗?混合它们绝对没有问题;我通常将主要组件作为类组件编写,因为它们功能更强大,而小型附属组件作为功能组件。。。在需要时使用钩子。您不需要使用useRef存储超时引用。只需使用const timoutRef=setTimeout。。。在UseEffect中,不需要使用useRef存储超时引用。只需使用const timoutRef=setTimeout。。。在你的使用效果之内