React Hooks 实现计数器
Posted
技术标签:
【中文标题】React Hooks 实现计数器【英文标题】:React Hooks to Implement Counter 【发布时间】:2020-04-11 15:59:08 【问题描述】:我正在尝试在组件中使用 React 挂钩(useState 和 useEffect)来显示投票计数。用户单击上/下后,“投票”状态应该增加或减少,然后应用程序调用 API 来修补数据库中的“投票”计数。原始投票计数来自父组件传递的道具。但由于某种原因,投票的初始状态始终为 0——即使当我 console.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>
)
【问题讨论】:
将雕像的初始值分配给道具的初始值被认为是反模式。 如果questionApiService
正在调用远程服务或 API 端点,它可能需要是带有 await
的 async
或使用承诺模式。
所以问题只与组件的初始状态有关?
问题似乎只与初始状态有关(PATCH 有效,单击向上/向下按预期重新呈现屏幕上的计数)。问题是当我刷新页面时,屏幕上显示的投票计数会变回零,即使这不是从道具传递的 voteCount。
我是 React 新手,所以不知道将 props 作为初始状态传递是不好的。我选择这样做是因为这个“投票”组件在多个地方被重用。父组件总是获取一些数据(其中一部分是当前投票计数,这是我想要的初始状态)。我不确定是否有更好的方法来做到这一点...除了将“投票”功能作为自己的组件之外,我可以将其写入每个父级,以便它可以访问那里的数据?
【参考方案1】:
您需要将itemType
添加到useEffect
的依赖项中,因为您不能期望该道具在第一次渲染时可用,或者在整个组件生命周期中保持静态。在您当前的示例中,引用的 itemType
将始终在此函数第一次运行时引用其值。
正如其他人所提到的,从 props 设置状态初始值是禁止的。一旦你的组件收到它的道具,你可以通过使用单独的效果来设置状态来解决这个问题:
...
const [votes, setVotes] = useState(0);
...
useEffect(() =>
setVotes(voteCount);
, [voteCount]);
【讨论】:
这正是我需要做的!谢谢!一旦我有足够的声望投票,我会回来投票这个答案!【参考方案2】:您可以像@tobiasfreid 所说的那样使用useState()
来更改计数器状态,或者如果您想创建一个自定义挂钩来实现相同的功能。
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];
通过这种方式,您可以创建自定义挂钩。
【讨论】:
【参考方案3】: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>
【讨论】:
【参考方案4】:tobiasfried 下面的回答真的帮助了我。我在这里发布完整的解决方案代码,以防将来可以帮助其他人。当然,如果有人有改进此代码的建议,我很乐意听到。
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>
)
【讨论】:
以上是关于React Hooks 实现计数器的主要内容,如果未能解决你的问题,请参考以下文章
React Hooks + Firebase Firestore onSnapshot - 正确使用带有反应钩子的 Firestore 监听器