[Fixed] Trying to submit a user score with a button in react. Getting error message of saveScore is not a function

Issue

I am building a react application and part of the application is a quiz section. At the end of the quiz there is a button which can save the user score in the quiz to the database.

This is my express route

// @route    Put api/profile/saveScore/:id
// @desc     Save users quiz score to profile
// @access   Private

router.put('/saveScore/:topic_id', checkObjectId('topic_id'), auth, async (req, {params: {topic_id } }, res) => {

    const score = req.body.score
    const topic = topic_id

    const newUserTopic = {
      score,
      topic,
    }

  try {
    const profile = await Profile.findOne({ user: req.user.id });

    profile.topics.unshift(newUserTopic);

    await profile.save();

    res.json(profile)
    
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }

})

The express route works no bother in postman so thinking the issue must be more on the react side.

This is my action route


// Save Quiz Score to users profile

export const saveScore = (topicId, payload) => async (dispatch) => {
  try {
    const res = await api.put(`/profile/saveScore/${topicId}`, payload);

    dispatch({
      type: GET_PROFILE,
      payload: res.data
    });

    dispatch(setAlert('Topic Saved', 'success'));

  } catch (err) {
    const errors = err.response.data.errors;

    if(errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, 'danger')))
    }

    dispatch({
      type: PROFILE_ERROR,
      payload: { msg: err.response.statusText, status: err.response.status }
    });
  }
};

This is my component

import React, { useEffect, useState, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import QuizItem from './QuizItem';
import { getTopicById } from '../../actions/topic';
import { saveScore} from '../../actions/profile';
import { SaveScoreForm } from './SaveScoreForm';


const Quiz = ({ getTopicById, saveScore, topic: { topic, loading }, match }) => {
    useEffect(() => {
        getTopicById(match.params.id);
    }, [getTopicById, match.params.id])

    const [currentIndex, setCurrentIndex] = useState(0);
    const [score, setScore] = useState(0);
    const [showAnswers, setShowAnswers] = useState(false)

    const [formData, setFormData] = useState({ score })

    
    const handleAnswer = (answer) => {
        if(!showAnswers) {
            if(answer === topic[currentIndex].correct_answer) {
                setScore(score + 1);
            }
        }
        setShowAnswers(true);
    };

    const handleNextQuestion = () => {
        setShowAnswers(false);

        setCurrentIndex(currentIndex + 1);
    }

    console.log(currentIndex)

    const onChange = (e) => {
      setFormData({ ...formData, [e.target.name]: e.target.value })
    }

    const onSubmit = (e) => {
      e.preventDefault();

     const payload = new FormData();
        payload.append('score', formData.score)
        
        
        saveScore(payload, match.params.id);  
    }


  return topic.length > 0 ? (
    <div className='container'>
      {currentIndex >= topic.length ? (
        <Fragment>
     <SaveScoreForm topic={topic} score={score} />
                         <form
                onSubmit={e => onSubmit(e)}
            >
                <input 
                    type='hidden'
                    value={score}
                    onChange={(e) => onChange(e)}
                />

                <input type='submit' className='btn btn-primary1 my-1' />   
            </form>
     </Fragment>
  ) : (
     <QuizItem 
        key={topic.question} 
        topic={topic[currentIndex]}
        showAnswers={showAnswers}
        handleNextQuestion={handleNextQuestion}
        handleAnswer={handleAnswer} 
      />
  )}
    </div>
     ) : (
       <Spinner/>
  )
}

Quiz.prototype = {
    getTopicById: PropTypes.func.isRequired,
    topic: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
    topic: state.topic,
    showAnswers: state.showAnswers,
    handleNextQuestion: state.handleNextQuestion,
    handleAnswer: state.handleAnswer

})


export default connect(mapStateToProps, { getTopicById })(Quiz)

Child component

import React from 'react'


export const SaveScoreForm = ({ score, topic,  }) => {

    return (
        <div>
            <div className='bg-primary1 p-2 my-4'>
                <h1 className='large'>Review Your Score</h1>
                <p className="lead">Quiz ended! Your score is: {(score/topic.length) * 100}%</p>
                <p>Save your score to your profile or take the quiz again!</p>
            </div>
        </div>
    );
};

export default SaveScoreForm;

TypeError: saveScore is not a function

Any help or pointers in the right direction would be very much appreciated.

Thanks

Solution

You are importing import { saveScore} from '../../actions/profile';

But then you have this prop

const Quiz = ({ getTopicById, saveScore
// ----------------------------^

which is overriding saveScore in your components context. Unless you are passing a saveScore prop while initialising <Quiz> it’ll be undefined.
If you want to import the saveScore module just remove this prop variable.

Leave a Reply

(*) Required, Your email will not be published