Search code examples
reactjstypescriptreact-reduxredux-sagastateful

Can't dispatch typescript action for class based component


I am trying to use this boilerplate for my react typescript project. I am unable to configure right type of actions. It was easy with JavaScript. But I am new to TypeScript. I want to know why my code is not working. Also please suggest the best way to solve this problem.

Dashboard.tsx

interface IAction{
  type: string,
  payload: any,
}

interface IProps {
  // dashboard: DashboardState;
  dashboard: any;
  // Actions
  changeUserName: IAction;
}
interface IState {}

class Dashboard extends React.Component<IProps, IState> {
  componentDidMount(){
    console.log("____FOCUS____");
    console.log(this.props);
    this.props.changeUserName("NewName");
    console.log("____FOCUS____");
    // this.props.changeUserName('New Name');
  }
  render() {
    return (
      <>
        <Helmet>
          <title>Dashboard</title>
          <meta name="description" content="Description of Dashboard" />
        </Helmet>
        <DashboardComponent username={'somevalue'} />
        {/* <Div>{t('')}</Div> */}
      </>
    );
  }
}


const mapStateToProps = (state: any, ownProps: any) => ({
  dashbaord: selectDashboard(state),
});

const mapDispatchToProps = (dispatch: any, ownProps: any) => ({
  ...bindActionCreators({
    ...actions,
  }, dispatch),
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({key: sliceKey, reducer: dashboardReducer});
const withSaga = injectSaga({key: sliceKey, saga: dashboardSaga});

export default compose(
  withReducer,
  withConnect,
  withSaga,
)(Dashboard);

Types

Types that I am creating are also as in docs.

/* --- STATE --- */
export interface DashboardState {
  username: string;
}

export type ContainerState = DashboardState;

Slice

This is the slice I am creating. It is as described in docs.

export const initialState: ContainerState = {
  username: 'Initial Username',
};

const dashboardSlice = createSlice({
  name: 'dashboard',
  initialState,
  reducers: {
    changeUserName(state, action: PayloadAction<string>) {
      state.username = action.payload;
    },
  },
});

export const { actions, reducer, name: sliceKey } = dashboardSlice;

Solution

  • Problem

    I was no creating interfaces the right way. The method of dispatching class based components was same as before. Only problem was interfaces that are now right.

    Solution Code

    Dashboard.tsx

    interface IProps {
      initChangeUserName: Function;
      changeUserName: Function;
    }
    
    interface IState {}
    
    class Dashboard extends React.Component<IProps, IState> {
      render() {
        return (
          <>
            <Helmet>
              <title>Dashboard</title>
              <meta name="description" content="Description of Dashboard" />
            </Helmet>
            <DashboardComponent 
            username={'somevalue'}
            initChangeUserName={this.props.initChangeUserName}
            />
          </>
        );
      }
    }
    
    const mapStateToProps = (state: any, ownProps: any) => ({
      dashboard: selectDashboard(state),
    });
    
    const mapDispatchToProps = (dispatch: any, ownProps: any) => ({
      ...bindActionCreators({ ...actions }, dispatch),
    });
    
    const withConnect = connect(mapStateToProps, mapDispatchToProps);
    const withReducer = injectReducer({ key: sliceKey, reducer: dashboardReducer });
    const withSaga = injectSaga({ key: sliceKey, saga: dashboardSaga });
    
    export default compose(
      withReducer,
      withConnect,
      withSaga,
    )(Dashboard);
    
    

    DashboardComponent.tsx

    
    // ....
    // This was a functional component
    <UserNameForm initChangeUserName={props.initChangeUserName} />
    // ....
    
    

    UserNameForm.tsx

    // ....
    this.props.initChangeUserName(this.state.username);
    // ....