反应挂钩以实施计数器

时间:2019-12-19 04:11:35

标签: javascript reactjs react-hooks

我正在尝试在组件中使用React挂钩(useState和useEffect)来显示投票计数。用户单击上/下后,“ vote”状态应递增或递减,然后应用程序将对数据库中的“ vote”计数进行API调用PATCH。原始投票计数来自父组件传递的道具。但是出于某种原因,投票的初始状态始终为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>
    )
}

4 个答案:

答案 0 :(得分:0)

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

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

...
const [votes, setVotes] = useState(0);
...

useEffect(() => {
  setVotes(voteCount);
}, [voteCount]);

答案 1 :(得分:0)

您可以使用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]; 
}

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

答案 2 :(得分:0)

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>

答案 3 :(得分:0)

下面的

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>
    )
}