1
votes

Novice at this and following patterns from JavascriptServices site which uses Typescript with React/Redux amoung others.

Mine is a React Redux project.

I have a menu tsx file that displays a menu that is largely unaltered from the boilerplate offering from JavascriptServices called "NavMenu.tsx"

I wanted to use a couple of state variables "IsAuthorised" & "username". These are in redux state and I want to just use them not set them etc.

I am getting the following error on the connect statement at the bottom and in particular (NavMenu) is red underscored and have no idea how to fix this error?

[ts] Argument of type 'typeof NavMenu' is not assignable to parameter of type 'Component & LoginState>'. Type 'typeof NavMenu' is not assignable to type 'StatelessComponent & LoginState>'. Type 'typeof NavMenu' provides no match for the signature '(props: DispatchProp & LoginState & { children?: ReactNode; }, context?: any): ReactElement'.

Here is the code for the NavMenu class - the error is on the very last line:

  import * as React from "react";
  import { NavLink, Link } from "react-router-dom";
  import {
    Navbar,
    Nav,
    NavItem,
    NavDropdown,
    MenuItem,
    Glyphicon
  } from "react-bootstrap";
  import { LinkContainer } from "react-router-bootstrap";
  import { connect } from "react-redux";
  import { ApplicationState } from "../store";
  import * as LoginState from "../store/Login";

  // At runtime, Redux will merge together...
  type NavMenuProps = LoginState.LoginState;

  export class NavMenu extends React.Component<NavMenuProps, {}> {
    public render() {
      return (
        <div className="main-nav">
          <div className="navbar navbar-inverse">
            <div className="navbar-header">
              <button
                type="button"
                className="navbar-toggle"
                data-toggle="collapse"
                data-target=".navbar-collapse"
              >
                <span className="sr-only">Toggle navigation</span>
                <span className="icon-bar" />
                <span className="icon-bar" />
                <span className="icon-bar" />
              </button>
              <Link className="navbar-brand" to={"/"}>
                JobsLedger_API
              </Link>
            </div>
            <div className="clearfix" />
            <div className="navbar-collapse collapse">
              <ul className="nav navbar-nav">
                <li>
                  <NavLink exact to={"/"} activeClassName="active">
                    <span className="glyphicon glyphicon-home" /> Home
                  </NavLink>
                </li>
                <li>
                  <NavLink to={"/counter"} activeClassName="active">
                    <span className="glyphicon glyphicon-education" /> Counter
                  </NavLink>
                </li>
                <li>
                  <NavLink to={"/fetchdata"} activeClassName="active">
                    <span className="glyphicon glyphicon-th-list" /> Fetch data
                  </NavLink>
                </li>
              </ul>
            </div>
          </div>
        </div>
      );
    }
  }

  export default connect(
    (state: ApplicationState) => state.login // Selects which state properties are merged into the component's props
  )(NavMenu) as typeof NavMenu;

enter image description here

EDIT

I note the 1st comment but need someone to expand on it.

The file I was following in the JavascriptServices example is below. I have followed their connect syntax.. there was no mention of mapStateToProps in that example...

Here it is:

            import * as React from 'react';
        import { Link, RouteComponentProps } from 'react-router-dom';
        import { connect } from 'react-redux';
        import { ApplicationState }  from '../store';
        import * as WeatherForecastsState from '../store/WeatherForecasts';

        // At runtime, Redux will merge together...
        type WeatherForecastProps =
            WeatherForecastsState.WeatherForecastsState        // ... state we've requested from the Redux store
            & typeof WeatherForecastsState.actionCreators      // ... plus action creators we've requested
            & RouteComponentProps<{ startDateIndex: string }>; // ... plus incoming routing parameters

        class FetchData extends React.Component<WeatherForecastProps, {}> {
            componentWillMount() {
                // This method runs when the component is first added to the page
                let startDateIndex = parseInt(this.props.match.params.startDateIndex) || 0;
                this.props.requestWeatherForecasts(startDateIndex);
            }

            componentWillReceiveProps(nextProps: WeatherForecastProps) {
                // This method runs when incoming props (e.g., route params) change
                let startDateIndex = parseInt(nextProps.match.params.startDateIndex) || 0;
                this.props.requestWeatherForecasts(startDateIndex);
            }

            public render() {
                return <div>
                    <h1>Weather forecast</h1>
                    <p>This component demonstrates fetching data from the server and working with URL parameters.</p>
                    { this.renderForecastsTable() }
                    { this.renderPagination() }
                </div>;
            }

            private renderForecastsTable() {
                return <table className='table'>
                    <thead>
                        <tr>
                            <th>Date</th>
                            <th>Temp. (C)</th>
                            <th>Temp. (F)</th>
                            <th>Summary</th>
                        </tr>
                    </thead>
                    <tbody>
                    {this.props.forecasts.map(forecast =>
                        <tr key={ forecast.dateFormatted }>
                            <td>{ forecast.dateFormatted }</td>
                            <td>{ forecast.temperatureC }</td>
                            <td>{ forecast.temperatureF }</td>
                            <td>{ forecast.summary }</td>
                        </tr>
                    )}
                    </tbody>
                </table>;
            }

            private renderPagination() {
                let prevStartDateIndex = (this.props.startDateIndex || 0) - 5;
                let nextStartDateIndex = (this.props.startDateIndex || 0) + 5;

                return <p className='clearfix text-center'>
                    <Link className='btn btn-default pull-left' to={ `/fetchdata/${ prevStartDateIndex }` }>Previous</Link>
                    <Link className='btn btn-default pull-right' to={ `/fetchdata/${ nextStartDateIndex }` }>Next</Link>
                    { this.props.isLoading ? <span>Loading...</span> : [] }
                </p>;
            }
        }

        export default connect(
            (state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props
            WeatherForecastsState.actionCreators                 // Selects which action creators are merged into the component's props
        )(FetchData) as typeof FetchData;
1
Your mapStateToProps function must return an object , aka {...}Meme Composer
Any updates on this? I'm having the same problem.alecardv

1 Answers

4
votes

Dummy is correct. Per the Redux docs:

[mapStateToProps(state, [ownProps]): stateProps] (Function): If this argument is specified, the new component will subscribe to Redux store updates. This means that any time the store is updated, mapStateToProps will be called. The results of mapStateToProps must be a plain object, which will be merged into the component’s props. If you don't want to subscribe to store updates, pass null or undefined in place of mapStateToProps.

Within mapStateToProps you are mapping parts of a Redux store to a local prop which you need to define. Most mapStateToProps calls look like this:

const mapStateToProps = (state) => {
    return {
        login: state.login
    };
};

Now the prop login is mapped to the login value in your Redux store. I am equally confused by the above code because state.weatherForecasts is not being assigned to any prop and therefore should not be accessible. I see they use a prop called forecasts but it's impossible to tell from their interface definition if that prop is handed down from the parent or supposed to be part of their store. I would stick with using mapStateToProps in the above way - it's pretty standard in the community and behaves consistently with TS.