Доступ к значениям редукционной формы через mapStateToProps - PullRequest
0 голосов
/ 28 мая 2020

Как мне получить доступ к значениям полей в родительском компоненте redux-form компонента?

Я не уверен, вызвано ли это typescript, но до того, как я начал использовать typescript, я смог получить доступ к значениям формы через mapStateToProps точно так же, как и сейчас. Я пытался выяснить, что отличалось от моей предыдущей реализации, но единственной разницей были бы версии зависимостей npm и добавление typescript.

LoginPage.tsx

import LoginForm from 'components/Forms/LoginForm'
import Layout from 'components/Layout'
import { StatusCodes } from 'lib/enums/statusCodes'
import { storeAuthToken } from 'lib/helpers/auth'
import { NextPage } from 'next'
import Router from 'next/router'
import React from 'react'
import { connect, DispatchProp } from 'react-redux'
import { FormInstance } from 'redux-form'

interface IProps {
  login: FormInstance<IFormData, IFormProps>
}

interface IState {
  errorMessage?: string,
  processing: boolean
}

interface IRootState {
  form: IProps
}

export interface IFormData {
  username?: string,
  password?: string
}

export interface IFormProps {
  contactId?: string,
  errorMessage?: string,
  fieldValues: Partial<IFormData>,
  processing: boolean
}

class LoginPage extends React.Component<NextPage & DispatchProp & IProps, IState> {
  state = {
    errorMessage: undefined,
    processing: false
  }

  setErrorMessage = (message: string) => {
    this.setState({
      errorMessage: message,
      processing: false
    })
  }

  handleSubmit = async (values: IFormData) => {
    if (values && values.username && values.password) {
      this.setState({
        errorMessage: undefined,
        processing: true
      })
  
      try {
        const { dispatch } = this.props
  
        await storeAuthToken(dispatch, values.username, values.password)
        Router.push('/')
      } catch (error) {
        if (error === StatusCodes.BAD_REQUEST) {
          this.setErrorMessage("Sorry, you have entered incorrect details. Please try again.")
        } else {
          this.setErrorMessage("Sorry, there was an issue trying to log you in")
        }
      }
    }
  }

  render() {
    const { login } = this.props
    const { processing } = this.state

    return (
      <Layout title="Login">
        <div className="form-wrapper full">
          <LoginForm processing={processing} onSubmit={this.handleSubmit} fieldValues={login.values} />
        </div>
      </Layout>
    )
  }
}

const mapStateToProps = ({ form: { login } }: IRootState) => ({ login })

export default connect(mapStateToProps)(LoginPage)

LoginForm.tsx

import Link from 'next/link'
import React from 'react'
import { Field, InjectedFormProps, reduxForm } from 'redux-form'
import FormButton from 'components/Forms/FormButton'
import Input from 'components/Fields/Input'
import { validateRequired } from 'lib/helpers/validators'
import { IFormProps, IFormData } from 'pages/login'

class LoginForm extends React.Component<IFormProps & InjectedFormProps<IFormData, IFormProps>> {
  render() {
    const { contactId, errorMessage, fieldValues, handleSubmit, processing } = this.props

    return (
      <form id="login" onSubmit={handleSubmit} >
        <h1>Sign in</h1>
        <fieldset>
          <div className="fields">
            {
              !contactId
                ? <Field name="username" type="text" component={Input} label="Username" validate={validateRequired} />
                : <Field name="username" type="email" component={Input} label="Email" validate={validateRequired} />
            } 
          </div>
          <div className="fields">
            <Field name="password" type="password" component={Input} label="Password" validate={validateRequired} />
          </div>
        </fieldset>
        { errorMessage && <p className="error-message">{errorMessage}</p> }
        <div className="form-bottom">
          <Link href="/"/*{`/forgot-password${fields.email ? `?email=${encodeURIComponent(fields.email)}` : ''}`}*/>
            <a className="inline">Forgotten your password?</a>
          </Link>
          <FormButton loading={processing}>
            Login
          </FormButton>
        </div>
      </form>
    )
  }
}

export default reduxForm<{}, IFormProps>({ form: 'login' })(LoginForm)

Вот мой файл хранилища redux, если он закодирован неправильно

import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'

export interface State {
  auth: AuthState
  contact: ContactState
}

const bindMiddleware = (middleware: [ThunkMiddleware]) => {
  if (process.env.NODE_ENV !== 'production') {
    const { composeWithDevTools } = require('redux-devtools-extension')
    return composeWithDevTools(applyMiddleware(...middleware))
  }

  return applyMiddleware(...middleware)
}

const combinedReducer = combineReducers({
  auth: authReducer,
  contact: contactReducer,
  form: formReducer
})

const reducer: Reducer = (state: State, action: AnyAction) => {
  if (action.type === HYDRATE) {
    const nextState: Reducer = {
      ...state,
      ...action.payload
    }

    return nextState
  } else {
    return combinedReducer
  }
}

const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))

export const wrapper = createWrapper<State>(makeStore/*, { debug: true }*/)

1 Ответ

0 голосов
/ 02 июня 2020

Похоже, я пропустил ключ в интерфейсе IApplicationState, и, как упоминалось в @cbr, параметры state и action необходимо было передать в combinedReducer, даже если он напрямую не принимает никаких .

Кроме того, ему не нравилось, когда константа nextState имела тип Reducer, поэтому я также изменил его на CombinedState<State>

Измененный код выглядит так

import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'

export interface State {
  auth: AuthState
  contact: ContactState,
  form: FormStateMap
}

const bindMiddleware = (middleware: [ThunkMiddleware]) => {
  if (process.env.NODE_ENV !== 'production') {
    const { composeWithDevTools } = require('redux-devtools-extension')
    return composeWithDevTools(applyMiddleware(...middleware))
  }

  return applyMiddleware(...middleware)
}

const combinedReducer = combineReducers({
  auth: authReducer,
  contact: contactReducer,
  form: formReducer
})

const reducer: Reducer = (state: State, action: AnyAction) => {
  if (action.type === HYDRATE) {
    const nextState: CombinedState<State> = {
      ...state,
      ...action.payload
    }

    return nextState
  } else {
    return combinedReducer(state, action)
  }
}

const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))

export const wrapper = createWrapper<State>(makeStore)
...