0
votes

Heyo this is my first time using react/redux for a project and so I've been migrating my "navigation" to a redux style implementation where I (at the start here) want to change a store state (activePage) on the click of a button, and then my App should render whatever page is active through that activePage state.

I'm stuck (should emphasize that I'm not sure what is overboard/overwriting stuff or missing for this, I've followed a few online tutorials for action/reducer/store type stuff (and I was going to do a dispatch call but it seems like I can call changePage right from the button click instead of dispatching (had problems implementing the dispatch)) and I've been banging my head against the desk as to how action is undefined when import it...perhaps I'm not looking at it correctly...am I missing any data that would help diagnose this error?:

TypeError: Cannot read property 'type' of undefined allReducers .../client/src/redux/reducer/index.js:9 6 | activePage: 'HomePage',

7 | }

8 | function allReducers(state = initState, action){

9 | switch(action.type){

10 | case CHANGE_PAGE:

11 | return{

12 | ...state,

loginButton.js

    const mapDispatchToProps = (state) => {
        return {
            changePage : state.activePage
        }
    };
    function LoginButtonThing (){
            return(
                <div>
                    <button onClick={() =>(changePage('loginPage'))}>Login Page</button>
                </div>
            )
    
    }
    //export default LoginButtonThing;
    export default connect(null,mapDispatchToProps)(LoginButtonThing);

actions.js

import { 
    CHANGE_PAGE, 
 } from "../constants/action-types";
export const changePage = (activePage) => ({
    type: CHANGE_PAGE,
    payload: {
        activePage,
    },
});

action-types.js

export const CHANGE_PAGE = "CHANGE_PAGE";

reducer/index.js

import {CHANGE_PAGE} from "../constants/action-types";
import {changePage} from "../actions/actions"
const initState = {
    activePage: 'HomePage',
}
function allReducers(state = initState, action){
    switch(action.type){
        case CHANGE_PAGE:
            return{
                ...state,
                activePage :action.payload.activePage,
            };
    }
}

//const store = createStore(rootReducer);

export default allReducers;

App.js

class App extends React.Component {
  render() {
    //this.props.activePage = 'HomePage';
    let renderedPage;
    if (this.props.changePage === "LoginPage") renderedPage = <LoginPage/>;
    else if (this.props.changePage === "HomePage") renderedPage = <HomePage/>;
    else renderedPage = <HomePage/>;
    //defaultStatus:activePage = "HomePage";
    return (
        <div id="App">
          {renderedPage}
        </div>
    );
  }
}

index.js

import {createStore, combineReducers} from 'redux';
import allReducers from "./redux/reducer"
//import LoginPage from "./loginPage";
import {Provider} from "react-redux";

const store = createStore(
    allReducers,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

ReactDOM.render(
    <Provider store = {store}>
        <React.StrictMode>
            <BrowserRouter>
                 <Route exact path="/" component = {HomePage}>
                    <Route path= "/loginPage" component ={LoginPage} />
                </Route>
             </BrowserRouter>
         <App />

  </React.StrictMode>
    </Provider>,
  document.getElementById('root')
);
1

1 Answers

0
votes

Missing Default Case

Your reducer needs to return state if an unknown action is dispatched, such as the "init" action which sets your state to the initial value.

function allReducers(state = initState, action) {
  switch (action.type) {
    case CHANGE_PAGE:
      return {
        ...state,
        activePage: action.payload.activePage
      };
    default:
      return state;
  }
}

Mixing up State & Dispatch

The connect HOC takes two arguments: mapStateToProps and mapDispatchToProps. I think the names are self-explanatory. So why does your mapDispatchToProps function take state as an argument instead of dispatch?

If using connect, the LoginButtonThing should access a version of changePage which is already bound to dispatch from its props. You can use the action object shorthand like this:

import { connect } from "react-redux";
import { changePage } from "../store/actions";

function LoginButtonThing({ changePage }) {
  return (
    <div>
      <button onClick={() => changePage("loginPage")}>Login Page</button>
    </div>
  );
}
export default connect(null, { changePage })(LoginButtonThing);

But you should use hooks, like this:

import { useDispatch } from "react-redux";
import { changePage } from "../store/actions";

export default function LoginButtonThing() {
  const dispatch = useDispatch();
  return (
    <div>
      <button onClick={() => dispatch(changePage("loginPage"))}>Login Page</button>
    </div>
  );
}

Inconsistent Naming

This doesn't actually navigate to the LoginPage component! That's because LoginButtonThing updated the page to "loginPage" (lowercase), but App is looking for the string "LoginPage" (uppercase).


Don't Do This

There is a lot that's wrong with your code and a lot that's just sort of "dated" and we have better ways now, like hooks and Redux-Toolkit.

I actually think that moving navigation state into Redux like you are trying to do is not a good idea and I would recommend that you stick with react-router. You can read here why you don't need or want to do this.