对React钩子不太熟练,以前使用过很多类组件,希望您能原谅。
当前代码会导致无限次重新渲染,我想我理解为什么-整个函数体都在重新渲染时被调用。
const NavTabs = () => {
const classes = useStyles();
const [categories, setCategories] = React.useState();
const axiosPromise = getRequest(consts.categoriesURL);
axiosPromise.then(data => {
setCategories(data.value);
})
return (
<div className={classes.root}>
<AppBar position="static">
</AppBar>
{categories && <DynamicTabs categories={categories}/>}
</div>
);
}
我想我可以做类似if (!categories) { const axiosPromise [...]
之类的事情,即只有在尚未填充类别时才执行http请求。我猜这也可以通过useEffect
解决吗?还是将挂钩包装在内部函数中?
我猜我真正的问题是-为什么React会重新渲染整个函数体?它不应该只渲染return函数吗?那么使用将在每个渲染器上重新运行的钩子有什么意义呢?
与类组件相比-函数主体中的代码是否不应该与类组件中的构造函数代码相同,而返回函数是否应与render方法等效?
答案 0 :(得分:2)
我想我可以执行if(!categories){const axiosPromise [...]等操作,即仅在尚未填充类别时才执行http请求。我想这也可以通过useEffect解决吗?还是将挂钩包装在内部函数中?
是的,useEffect是前往此处的方法。发出请求并将结果设置为state是副作用,在这种情况下,只应运行一次。我们可以使用useEffect轻松实现。
我猜我真正的问题是-为什么React会重新渲染整个函数体?它不应该只渲染return函数吗?那么使用将在每个渲染器上重新运行的钩子有什么意义呢?
React无法拆分js函数,而只能重新呈现返回值。该函数是原子函数,必须完成。这就是钩子的作用。运行钩子时使用React控件,以便它可以执行有趣的操作,例如批处理状态更新,忽略过时的效果并优先处理诸如动画之类的高优先级工作。
与类组件相比-函数主体中的代码是否不应该与类组件中的构造函数代码相同,而返回函数是否应与render方法等效?
功能组件等效于类组件的render方法。它们的调用方式类似。所有其他生命周期方法都由钩子代替。
我建议react docs是一个很好的起点,而Dan Abramov拥有great deep dive on hooks。
答案 1 :(得分:1)
是的,getRequest
在每个渲染周期都被调用,这会设置一些状态并触发重新渲染。最好将它放在带有依赖项数组的效果挂钩中。您定义的依赖项将决定何时可以调用getRequest
。
为什么React会重新渲染整个函数体?
需要运行整个函数主体才能确定返回值。
然后使用将在每个渲染器上重新运行的钩子有什么意义?
挂钩在每个渲染器上运行,其定义顺序相同,但是取决于依赖项可能不会调用回调。钩子使功能组件具有如此高的生存能力和组件生命周期的感觉,几乎等同于功能上基于类的组件。在大多数情况下,您可以将基于类的组件完全转换为功能组件,而不会删除任何功能。
与类组件相比-函数主体中的代码是否不应该与类组件中的构造函数代码相同,而返回函数是否应与render方法等效?
将整个功能组件定义 认为是基于类的render
函数,可以包含一些逻辑并返回计算出的JSX以呈现到DOM。
示例解决方案:
const NavTabs = () => {
const classes = useStyles();
const [categories, setCategories] = React.useState(); // <-- no initial state!
useEffect(() => {
getRequest(consts.categoriesURL).then(data => {
setCategories(data.value); // <-- will update state and trigger render
});
}, []); // <-- empty dependency is run once on component mount
return (
<div className={classes.root}>
<AppBar position="static">
</AppBar>
{categories && <DynamicTabs categories={categories}/>}
</div>
);
}
答案 2 :(得分:1)
要回答“为什么反应正在运行整个功能”,答案是javascript函数是这样工作的:您总是必须运行整个程序,它们不会停在中间*。如果您习惯对组件进行分类,那么我了解您在这里的想法:我没有构造器部分和渲染部分吗?答案是:如果您正在使用功能组件,那不是真的。您只有渲染。但是钩子是魔术,它们让您假装有两个部分。
挂钩知道何时调用它们,并且假设您始终以相同的顺序调用它们,则可以跟踪render函数外部的状态。所以工作的方式有点像这样:
魔术是渲染上下文可以用钩子做一些奇特的事情,例如只运行一次,总是从钩子或任何其他数目的事物返回相同的值。但是从某种意义上说,组件“类”成为了内部反应的渲染上下文,钩子知道如何访问。
这是在类组件中实现的useState
钩子的示例:(您永远不需要这样做,但这是钩子如何工作的示例)。
class FakeHook extends React.Component {
constructor(...args) {
super(...args)
this.state = {}
this.useStateCalls = 0
}
useState(defaultValue){
const currentRenderContext = this.state
let value = defaultValue
const currentStateKey = `useState${this.useStateCalls}`
if (currentStateKey in currentRenderContext) value = currentRenderContext[currentStateKey]
this.useStateCalls++
return[value, (newValue) => this.setState({[currentStateKey]: newValue})]
}
render(){
this.useStateCalls = 0
let [fooState, setFoo] = this.useState("foo default")
let [barState, setBar] = this.useState("bar default")
return(
<dl>
<dt>Foo state</dt>
<dd>
<strong>Value:</strong>
<div>{fooState}</div>
<button onClick={(event) => {event.preventDefault(); setFoo(`foo updated at ${new Date().toLocaleString()}`)}}>Update Foo</button>
</dd>
<dt>Bar state</dt>
<dd>
<strong>Value:</strong>
<div>{barState}</div>
<button onClick={(event) => {event.preventDefault(); setBar(`bar updated at ${new Date().toLocaleString()}`)}}>Update Bar</button>
</dd>
<dt>Render context state:</dt>
<dd><pre>{JSON.stringify(this.state)}</pre></dd>
</dl>
)
}
}
ReactDOM.render(<FakeHook/>, document.getElementById('main'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main id=main>loading or error occurred...</main>
请注意,状态是根据在render内调用钩子的顺序存储的。在实际的挂钩中,渲染上下文存储在this.state
之外的其他位置,但是挂钩知道如何获取它,因此您并不在乎。而且,这只是一个例子,实际的钩子工作原理略有不同,但是概念是相同的。
*:async
函数和生成器不会一次全部运行,而是返回一个特殊对象,该对象使函数分多个步骤运行,在await
或{{1}上等待或暂停}。