(Error) Passing data from one component to another using React-Redux mapStateToProps + Reducers

93 Views Asked by At

After spending hours trying various methods of passing data from one component to another I am completely lost as to why the code below doesn't work.

Purpose: I am simply trying to pass a "courseID" (a string) to the app's state during a "NavLink" button click; giving the component being opened up by the Navlink the ability to read this string via the state.

Based on console.log messages, it appears that the "selectedCourseAction.js" is being triggered with the correct courseID value, but the selectedCourseReducer always displayed the initial value and doesn't actually update.

CourseDetails.js

import React from 'react';
import { connect } from 'react-redux';
import { firestoreConnect } from 'react-redux-firebase';
import { compose } from 'redux';
import { NavLink } from 'react-router-dom'; //Use NavLink rather than Link to gain access to the "active" class when the link is pressed.
import { selectedCourseID } from '../../store/actions/selectedCourseActions'


const CourseDetails = (props) => {


    const { ChosenCourse, removeCourse} = props;


    (...)

        console.log(props.match.params.id); <--------- Displays the currently selected Course Id.

        return(

            <div>

               (...)
                 
               //On NavLink click below, the idea is to pass the "course Id" of this component (displayed in 'props.match.params.id') into the "createLecture" component which the NavLink is trying to open via the "selectedCourseID" function found in "selectedCourseActions.js"
           
      <NavLink onClick={ () => selectedCourseID(props.match.params.id)} to={'/createlecture'} 
      className='btn btn-floating custom-orange lighten-1'>New Lecture</NavLink>

               (...)

        )

}

const mapStateToProps = (state, ownProps) => {

    const courseId = ownProps.match.params.id;

    console.log(courseId);   <------------------ Displays the currently selected Course Id.

     (...)
    
  
    return {
        selectedCourseID: courseId,   <------------This appears to trigger the selectedCourseAction.js
        (...)
        
    }

}

const mapDispatchToProps = (dispatch) => {

 (...)

}




export default compose(connect(mapStateToProps, mapDispatchToProps), firestoreConnect([
    {   
        (...)

    }
    
])
)(CourseDetails)

The selectedCourseActions.js

export const selectedCourseID = (CourseId) => {

    console.log('Inside SelectedCourseActions.js: ' + CourseId); <------- This is triggered when the NavLink in the "CourseDetails.js" component is clicked and this displays the CourseId.
    return {

        type: 'UPDATE_SELECTED_COURSE_ID', <--------- This leads to selectedCourseReducer.js
        SelectedCourseID: CourseId          <--------- Intending to pass this SelectedCourseID value into the selectedCourseReducer.js, to save its value in the state of the application for later retrieval.

    }
    
};

The selectedCourseReducer.js


const initState = 'Initial State of SelectedCourseReducer'; <---- Just temporary string to indicate if the initialState of the reducer is ever changed.


const selectedCourseReducer = (state=initState, action) => {
    


    switch (action.type) {
        
        case 'UPDATE_SELECTED_COURSE_ID':
            console.log('Updating Selected Course ID inside selectedCourseReducer: ', action.selectedCourseID); <------- THIS IS NEVER REACHED.  The default below is always called.
            return action.selectedCourseID;  <------- Trying to return the updated selectedCourseID value onto the state of the application.

        default:
            console.log('Hit Default State inside selectedCourseReducer: ', action.selectedCourseID); <----- THIS IS ALWAYS REACHED when this reducer is called with the "action.selectedCourseID" of "undefined".
            return state;
           
    }
    
}


export default selectedCourseReducer

FOR FUTURE VIEWERS, HERE IS THE SOLUTION (inspired by markerikson's response)

You need to introduce the selectedCourseID into mapDispatchToProps of the "CourseDetails.js" component. (see some of the steps/code below)

Import useDispatch()

import { useDispatch } from 'react-redux';

introduce the selectedCourseID and dispatch = useDispatch();

(...) 

const { ChosenCourse, removeCourse, selectedCourseID} = props;
    const dispatch = useDispatch();

(...)

Change the NavLink to reference the dispatch

<NavLink onClick={ () => dispatch(selectedCourseID(props.match.params.id))} to={'/createlecture'} ... 

Add the dispatch in mapDispatchToProps

const mapDispatchToProps = (dispatch) => {

    return {
        (...) 
        selectedCourseID: (courseId) => dispatch(selectedCourseID(courseId)),
    }

}
export default compose(connect(mapStateToProps, mapDispatchToProps), firestoreConnect([
    {   
        (...)

    }
    
])
)(CourseDetails)
1

There are 1 best solutions below

3
On BEST ANSWER

Because you're not dispatching an action. You're calling the selectedCourseID action creator, but the action is never being passed to store.dispatch:

 onClick={ () => selectedCourseID(props.match.params.id)}

You should pass the selectedCourseID action creator through the mapDispatch argument, ideally using the "object shorthand" form of mapDispatch:

const mapDispatch = {selectedCourseID};

// later
export default connect(mapState, mapDispatch)(MyComponent);

and then access it as props.selectedCourseID, either literally that way or via destructuring:

const { ChosenCourse, removeCourse, selectedCourseID } = props;

Alternately, consider using the React-Redux hooks API instead of connect, which we generally recommend at this point:

const CourseDetails = (props) => {
  const dispatch = useDispatch();

  // later

  return <NavLink onClick={ () => dispatch(selectedCourseID(props.match.params.id))} />
}