admin管理员组文章数量:1326280
I'm trying to use React hooks (useState & useEffect) in a ponent to show a vote count. After the user clicks up/down the 'vote' state should be increment or decremented and then the app makes an API call to PATCH the "votes" count in the database. The original vote count is ing from props passed from the parent ponent. But for some reason, the initial state for the votes is always 0 -- even though when I console.log the props it shows the correct number of votes?
PATCH request seems to be working fine, but something with hooks I think is wrong. Thanks in advance for any help I can get on this!
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>
)
}
I'm trying to use React hooks (useState & useEffect) in a ponent to show a vote count. After the user clicks up/down the 'vote' state should be increment or decremented and then the app makes an API call to PATCH the "votes" count in the database. The original vote count is ing from props passed from the parent ponent. But for some reason, the initial state for the votes is always 0 -- even though when I console.log the props it shows the correct number of votes?
PATCH request seems to be working fine, but something with hooks I think is wrong. Thanks in advance for any help I can get on this!
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>
)
}
Share
Improve this question
asked Dec 19, 2019 at 4:11
LydiaHendriksLydiaHendriks
1,6203 gold badges15 silver badges19 bronze badges
6
- 1 Assigning initial value of statue to that of props is considered anti patter. – Atin Singh Commented Dec 19, 2019 at 4:14
-
1
If
questionApiService
is calling a remote service or API endpoint it might need to be anasync
withawait
or use a promise pattern. – react-dev Commented Dec 19, 2019 at 4:20 - 1 So the problem is only related to initial state of the ponent? – Loi Nguyen Huynh Commented Dec 19, 2019 at 4:30
- The problem seems to be only related to the initial state (PATCH works, and click up/down rerenders the count on the screen as expected). The problem is that when I refresh the page the vote count that is shown on the screen goes back to zero, even though that is not the voteCount passed from props. – LydiaHendriks Commented Dec 19, 2019 at 5:01
- I'm new to React so didn't know that passing props as initial state was not good. I chose to do that since this 'Votes' ponent is reused in several places. The parent ponents always fetch some data (part of that being the current vote count which is what I want the initial state to be). I'm not sure of a better way to do this...other than instead of making the 'vote' function it's own ponent, I could write it into each parent so that it can access the data there? – LydiaHendriks Commented Dec 19, 2019 at 5:01
4 Answers
Reset to default 3You need to add itemType
to the dependencies of useEffect
, since you can't expect the prop to be available on the very first render, or to remain static throughout the ponent lifecycle. With your current example, the itemType
referenced will always refer to its value at the very first time this function was run.
Also as others have mentioned, setting state initial value from props is a no-no. You can solve this by using a separate Effect to set state once your ponent receives its props:
...
const [votes, setVotes] = useState(0);
...
useEffect(() => {
setVotes(voteCount);
}, [voteCount]);
You can use useState()
as @tobiasfreid said to change the counter state or if you want to create a custom hook to implement the same functionality.
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];
}
In this way, you can create your custom hooks.
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./ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
tobiasfried's answer below really helped me out. I'm posting the full solution code here in case it can help anyone else out in the future. And of course, if anyone has suggestions to improve this code I'd love to hear them.
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>
)
}
本文标签: javascriptReact Hooks to Implement CounterStack Overflow
版权声明:本文标题:javascript - React Hooks to Implement Counter - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742210097a2433552.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论