反应钩子来实现计数器



我正在尝试在组件中使用React钩子(useState&useEffect(来显示投票计数。用户单击向上/向下单击后,"投票"状态应递增或递减,然后应用进行 API 调用以修补数据库中的"投票"计数。原始投票计数来自从父组件传递的 props。但是由于某种原因,投票的初始状态始终是0 - 即使当我安慰时.log道具它显示了正确的票数?

PATCH 请求似乎工作正常,但我认为带有钩子的东西是错误的。提前感谢我能得到的任何帮助!

export default function Votes(props) {
const { item, voteCount, itemType } = props
const [votes, setVotes] = useState(voteCount || 0)
useEffect(() => {
if (itemType == 'question') {
questionApiService.updateQuestionFields({
questionId: item.id, 
questionFields : { votes: `${votes}` }
})
} else if (itemType === 'answer') {
console.log('answer')
// questionApiService.updateAnswerFields({
//     answerId: item.id,
//     answerFields: { votes: votes }
//})
}
}, [votes])
const handleClick = e => {
e.currentTarget.id === "increment"
? setVotes(prevCount => prevCount + 1)
: setVotes(prevCount => prevCount - 1)
}
return (
<div className="QuestionPage__votes-count">
<button id="increment" onClick={handleClick}>
<FontAwesomeIcon icon={faCaretUp} size="2x" />
</button>
{votes}
<button id="decrement" onClick={handleClick}>
<FontAwesomeIcon icon={faCaretDown} size="2x" />
</button>
</div>
)
}

您需要将itemType添加到useEffect的依赖项中,因为您不能期望该道具在第一次渲染时可用,或者在整个组件生命周期中保持静态。对于当前示例,引用的itemType将始终在首次运行此函数时引用其值。

同样正如其他人所提到的,从道具设置状态初始值是不行的。您可以通过使用单独的效果来解决此问题,以便在组件收到其属性后设置状态:

...
const [votes, setVotes] = useState(0);
...
useEffect(() => {
setVotes(voteCount);
}, [voteCount]);

您可以使用useState()@tobiasfreid来更改计数器状态,或者如果您想创建自定义钩子来实现相同的功能。

function ChangeCounter() {
const [counter,onChangeValue] = useCounter(0);
return (
<div className="Form">
<button type="button" onClick={onChangeValue(counter + 1)}> Increase Counter </button>
</div>
);
}
function useCounter(initialValue){
const [value, setValue] = useState(initialValue || 0);
const onChange= (data) => {
setValue(data)
};
return [value, onChange]; 
}

通过这种方式,您可以创建自定义钩子。

import React from 'react';
import ReactDOM from 'react-dom';
function useCounter(initialCount = 0) {
const [count, setCount] = React.useState(initialCount);
const increment = React.useCallback(() => setCount(c => c + 1), []);
const decrement = React.useCallback(() => setCount(c => c - 1), []);
return { count, increment, decrement };
}
const delay = duration => new Promise(resolve => setTimeout(resolve, duration));
function App() {
const { count: votes, increment, decrement } = useCounter(0);
React.useEffect(() => {
(async () => {
console.log('Call your API');
await delay(2000);
console.log('API Called with votes :', votes);
})();
}, [votes]);
return (
<div>
<h1>Votes {votes}</h1>
<button onClick={decrement}>Decrement</button>
<button onClick={increment}>Increment</button>
</div>
);
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
<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>

托比亚斯弗里德下面的回答真的帮助了我。我在这里发布完整的解决方案代码,以防将来可以帮助其他人。当然,如果有人有改进此代码的建议,我很想听听。

export default function Votes(props) {
const { item, itemType } = props
const [votes, setVotes] = useState()
useEffect(() => {
setVotes(item.votes);
}, [item.votes])
useEffect(() => {
if (itemType == 'question') {
questionApiService.updateQuestionFields({
questionId: item.id, 
questionFields : { votes: votes }
})
} else if (itemType === 'answer') {
// questionApiService.updateAnswerFields({
//     answerId: item.id,
//     answerFields: { votes: votes }
//})
}
}, [votes, itemType])
return (
<div className="QuestionPage__votes-count">
<button 
onClick={() => setVotes(prevCount => prevCount + 1)}>
<FontAwesomeIcon icon={faCaretUp} size="2x" />
</button>
{votes}
<button 
onClick={() => setVotes(prevCount => prevCount - 1)}>
<FontAwesomeIcon icon={faCaretDown} size="2x" />
</button>
</div>
)
}

最新更新