对 usEeffect 和 useState 的函数组件问题做出反应
react function component issue with usEffect and useState
有时我必须使用一些原生的js libaray api,所以我可能有这样一个组件:
function App() {
const [state, setState] = useState(0)
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1.innerHTML = 'h1h1h1h1h1h1'
container.append(h1)
h1.onclick = () => {
console.log(state)
}
}, [])
return (
<div>
<button onClick={() => setState(state => state + 1)}>{state}</button>
<div id="container"></div>
</div>
)
}
以上是一个简单的例子。我应该在安装反应后初始化库,并绑定一些事件处理程序。问题来了:如上所示,如果我使用 useEffect()
而没有 state
作为 dependencies 数组中的项目,则 onclick 处理程序中的值 state
可能永远不会改变。但是如果我将 state
添加到 dependencies 数组,效果函数将在每次状态更改时执行。上面是一个简单的例子,但是真正的库的初始化可能会非常昂贵,所以这种方式是不行的。
现在我找到了 3 种方法来解决这个问题,但其中 none 令我满意。
- 创建一个 ref 来保持状态,并添加一个效果来改变它
current
每次 state
改变。 (一个额外的变量和效果)
- 与第一个一样,但在函数外定义一个变量而不是
ref
。 (部分为首)
- 使用class组件。 (这个太多了)
那么有没有解决问题并使代码更好的解决方案?
我认为您已经很好地总结了这些选项。我只想添加一个选项,即您可以将代码拆分为一个初始化效果和一个仅更改 onclick 的效果。初始化逻辑可以 运行 一次,onclick 可以 运行 每次渲染:
const [state, setState] = useState(0)
const h1Ref = useRef();
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1Ref.current = h1;
// Do expensive initialization logic here
}, [])
useEffect(() => {
// If you don't want to use a ref, you could also have the second effect query the dom to find the h1
h1ref.current.onClick = () => {
console.log(state);
}
}, [state]);
此外,您可以稍微简化选项 #1。您无需创建 useEffect 来更改 ref.current
,您只需在组件主体中执行此操作即可:
const [state, setState] = useState(0);
const ref = useRef();
ref.current = state;
useEffect(() => {
const container = document.querySelector('#container');
// ...
h1.onClick = () => {
console.log(ref.current);
}
}, []);
有时我必须使用一些原生的js libaray api,所以我可能有这样一个组件:
function App() {
const [state, setState] = useState(0)
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1.innerHTML = 'h1h1h1h1h1h1'
container.append(h1)
h1.onclick = () => {
console.log(state)
}
}, [])
return (
<div>
<button onClick={() => setState(state => state + 1)}>{state}</button>
<div id="container"></div>
</div>
)
}
以上是一个简单的例子。我应该在安装反应后初始化库,并绑定一些事件处理程序。问题来了:如上所示,如果我使用 useEffect()
而没有 state
作为 dependencies 数组中的项目,则 onclick 处理程序中的值 state
可能永远不会改变。但是如果我将 state
添加到 dependencies 数组,效果函数将在每次状态更改时执行。上面是一个简单的例子,但是真正的库的初始化可能会非常昂贵,所以这种方式是不行的。
现在我找到了 3 种方法来解决这个问题,但其中 none 令我满意。
- 创建一个 ref 来保持状态,并添加一个效果来改变它
current
每次state
改变。 (一个额外的变量和效果) - 与第一个一样,但在函数外定义一个变量而不是
ref
。 (部分为首) - 使用class组件。 (这个太多了)
那么有没有解决问题并使代码更好的解决方案?
我认为您已经很好地总结了这些选项。我只想添加一个选项,即您可以将代码拆分为一个初始化效果和一个仅更改 onclick 的效果。初始化逻辑可以 运行 一次,onclick 可以 运行 每次渲染:
const [state, setState] = useState(0)
const h1Ref = useRef();
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1Ref.current = h1;
// Do expensive initialization logic here
}, [])
useEffect(() => {
// If you don't want to use a ref, you could also have the second effect query the dom to find the h1
h1ref.current.onClick = () => {
console.log(state);
}
}, [state]);
此外,您可以稍微简化选项 #1。您无需创建 useEffect 来更改 ref.current
,您只需在组件主体中执行此操作即可:
const [state, setState] = useState(0);
const ref = useRef();
ref.current = state;
useEffect(() => {
const container = document.querySelector('#container');
// ...
h1.onClick = () => {
console.log(ref.current);
}
}, []);