Mar*_*ano 20 javascript reactjs react-hooks
我正在玩React钩子并面临一个问题.当我尝试使用事件监听器处理的按钮来控制日志时,它显示错误的状态.
CodeSandbox: https ://codesandbox.io/s/lrxw1wr97m
为什么它显示错误的状态?在第一张卡中,Button2应在控制台中显示2张卡.有任何想法吗?
import React, { useState, useContext, useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const CardsContext = React.createContext();
const CardsProvider = props => {
const [cards, setCards] = useState([]);
const addCard = () => {
const id = cards.length;
setCards([...cards, { id: id, json: {} }]);
};
const handleCardClick = id => console.log(cards);
const handleButtonClick = id => console.log(cards);
return (
<CardsContext.Provider
value={{ cards, addCard, handleCardClick, handleButtonClick }}
>
{props.children}
</CardsContext.Provider>
);
};
function App() {
const { cards, addCard, handleCardClick, handleButtonClick } = useContext(
CardsContext
);
return (
<div className="App">
<button onClick={addCard}>Add card</button>
{cards.map((card, index) => (
<Card
key={card.id}
id={card.id}
handleCardClick={() => handleCardClick(card.id)}
handleButtonClick={() => handleButtonClick(card.id)}
/>
))}
</div>
);
}
function Card(props) {
const ref = useRef();
useEffect(() => {
ref.current.addEventListener("click", props.handleCardClick);
return () => {
ref.current.removeEventListener("click", props.handleCardClick);
};
}, []);
return (
<div className="card">
Card {props.id}
<div>
<button onClick={props.handleButtonClick}>Button1</button>
<button ref={node => (ref.current = node)}>Button2</button>
</div>
</div>
);
}
ReactDOM.render(
<CardsProvider>
<App />
</CardsProvider>,
document.getElementById("root")
);Run Code Online (Sandbox Code Playgroud)
我使用React 16.7.0-alpha.0和Chrome 70.0.3538.110
顺便说一句,如果我使用сlass重写CardsProvider,问题就消失了.CodeSandbox使用类:https://codesandbox.io/s/w2nn3mq9vl
Est*_*ask 35
这是使用useState钩子的功能组件的常见问题.
useState并setTimeout定义在setInterval功能组件的范围内.每次运行时都有新功能,它们指的CardsProvider是在定义它们时获得的状态.
Cardhandler在handleCardClickmount 上注册,它在整个组件生命周期内具有相同的功能,并且指的是在handleButtonClick定义时新鲜的陈旧状态.虽然CardsProvider在每个cards渲染上注册,但它每次都是新功能,并指新鲜状态.
解决此问题的常用方法是使用CardsProvider而不是handleCardClick.ref是一个基本配方,它提供了一个可以通过引用传递的可变对象.Card这里不适合,因为它不会重新渲染组件useEffect.
解决方法是使用可以通过引用传递的可变状态,类似于ref,例如:
const ref = useRef(0);
function eventListener() {
ref.current++;
}
Run Code Online (Sandbox Code Playgroud)
handleCardClick应该被用来代替handleButtonClick获得当前状态.
在这种情况下,已经存在一个对象(Card数组),它可以通过引用传递而不是不可变的:
const useForceUpdate = () => {
const [, setState] = useState();
return () => setState({});
}
const ref = useRef(0);
const forceUpdate = useForceUpdate();
function eventListener() {
ref.current++;
forceUpdate();
}
Run Code Online (Sandbox Code Playgroud)
小智 26
解决这个问题的更简洁的方法是创建一个我调用的钩子 useStateRef
function useStateRef(initialValue) {
const [value, setValue] = useState(initialValue);
const ref = useRef(value);
useEffect(() => {
ref.current = value;
}, [value]);
return [value, setValue, ref];
}
Run Code Online (Sandbox Code Playgroud)
您现在可以将ref用作状态值的引用。
对我来说简短的回答是 useState 有一个简单的解决方案:
function Example() {
const [state, setState] = useState(initialState);
function update(updates) {
// this might be stale
setState({...state, ...updates});
// but you can pass setState a function instead
setState(currentState => ({...currentState, ...updates}));
}
//...
}
Run Code Online (Sandbox Code Playgroud)
对我的简短回答
这不会在myvar 更改时触发重新渲染。
const [myvar, setMyvar] = useState('')
useEffect(() => {
setMyvar('foo')
}, []);
Run Code Online (Sandbox Code Playgroud)
这将触发渲染 -> 将myvar 放入 []
const [myvar, setMyvar] = useState('')
useEffect(() => {
setMyvar('foo')
}, [myvar]);
Run Code Online (Sandbox Code Playgroud)