"Uncaught TypeError: Невозможно прочитать свойство 'type' undefined" и неясно, с чем оно связано

Я изначально думал, что это проблема с react-redux, axios, lodash и / или react-dropzone; Тем не менее, файлы загружаются нормально, но эта ошибка все еще срабатывает. (Даже если в консоли написано "сбой", файлы отправляются на сервер).

Я начинаю думать, что проблема может быть связана с react-router-redux на основании того, что я прочитал здесь:

https://github.com/reactjs/react-router-redux/issues/182

Тем не менее, предложения не работают для меня.

Во всяком случае, я получаю Uncaught TypeError: Cannot read property 'type' of undefined что я не могу действительно сказать, что вызывает это или как решить это. Вот несколько скриншотов ошибок:

не определенолиния

Итак, вот что должно иметь отношение к react-router, Просто не уверен, что происходит.

Точка входа:

// ./react/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { routerMiddleware, ConnectedRouter } from 'react-router-redux';

import createHistory from 'history/createBrowserHistory';

// Render on every route
import App from './components/app';
import Navbar from './containers/global/navbar';
import Footer from './containers/global/footer';

// Reducers 
import rootReducer from './reducers';

// SCSS for the project
import styles from '../assets/scss/main.scss';

// IE polyfill error fix
require('es6-promise').polyfill();
var axios = require('axios');

const history = createHistory();

const initialState = {};
const enhancers = [];
const middleware = [thunk, routerMiddleware(history)];

if (process.env.NODE_ENV === 'development') {
    const devToolsExtension = window.devToolsExtension

    if (typeof devToolsExtension === 'function') {
        enhancers.push(devToolsExtension())
    }
}

const composedEnhancers = compose(applyMiddleware(...middleware), ...enhancers);
const protectedRoute = compose(Timers, RequireAuth);

const store = createStore(rootReducer, initialState, composedEnhancers);

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <div>
                <Navbar />
                <App />
                <Switch>
                      // various routes go here...
                </Switch>
                {/*<Footer />*/}
            </div>
        </ConnectedRouter>
    </Provider>
    , document.querySelector('.container'));

Глобальные редукторы и те, которые относятся к действию, которое вызывает ошибку.

// ./react/reducers/index.js
import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form';
import { routerReducer } from 'react-router-redux';
import documentReducer from './documents';

const rootReducer = combineReducers({
    form,
    router: routerReducer,
    documents: documentReducer,
});

export default rootReducer;

// ./react/reducers/documents.js
import {
    DOCUMENTS
} from '../actions/documents';

export default function(state = {}, action) {
    switch(action.type) {
        case DOCUMENTS:
            return { 
                ...state, 
                survey_id: action.payload.survey_id, 
            };
        default:
            return state;
    }

    return state;
}

Наконец, действие, которое вызывается при возникновении ошибки. Важно отметить: ошибка не возникает, когда axios.post не в _.map (фактически любой вид итерации массива). Однако, это только отправляет один файл, который не является поведением, за которым я следую.

// ./react/actions/documents.js
import _ from 'lodash';
import axios from 'axios';
import { push } from 'react-router-redux';
import { ROOT_URL } from '../../config/config.json';

// Establish the different types
export const DOCUMENTS = 'documents';

export function submitDocument(files) {
    const uploaders = _.map(files, f => {
        const formData = new FormData();

        formData.append('file', f);

        return axios.post(
            `${ROOT_URL}/api/documents/fileupload`,
            formData,
            { headers: 
                { 
                    'content-type': 'multipart/form-data',
                    'Authorization': 'JWT ' +  sessionStorage.getItem('token')
                }
            }            
        )

    });

    axios.
        all(uploaders)
        .then(response => {
            console.log('Success');
        })
        .catch(error => {
            console.log('Failed');
        })
}

Контейнер, который вызывает действие, вероятно, также будет полезен:

// ./react/containers/documents/submit_documents.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { submitDocument } from '../../actions/documents';
import Dropzone from 'react-dropzone';

class SubmitDocuments extends Component {

    constructor() {
        super();
        this.state = {
            filesToBeSent: [],
            filesPreview: [],
        }

        this.handleClick = this.handleClick.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.onDrop = this.onDrop.bind(this);
    }

    handleSubmit(event) {
        event.preventDefault();
        this.props.submitDocument(this.state.filesToBeSent);
    }

    onDrop(acceptedFiles) {
        var filesToBeSent = this.state.filesToBeSent;       
        _.map(acceptedFiles, f => {
            filesToBeSent.unshift(f);
        });

        filesToBeSent = _.uniqBy(filesToBeSent, 'name');

        var filesPreview = [];

        _.map(filesToBeSent, i => {
            filesPreview.unshift(
                <div key={i.name}>
                    <h5>{i.name} - {i.size} bytes</h5>
                </div>
            )            
        });

        this.setState({
            filesToBeSent,
            filesPreview
        });
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <div className='panel panel-default'>
                    <div className='panel-heading'>
                        <h4><strong>Submit Documents</strong></h4>
                    </div>

                    <div className='panel-body'>
                        <Dropzone className='dropzone' onDrop={this.onDrop}> 
                            <h3>Click to add files or drag files here to upload</h3>
                        </Dropzone>
                        {this.state.filesPreview}
                        <button type='submit' disabled={this.state.filesPreview.length < 1} className='btn btn-primary'>Submit</button>
                        <button type='button' className='btn btn-danger' onClick={this.handleClick}>Cancel</button>
                    </div>
                </div>
            </form>
        ); 
    }
}

function mapStateToProps(state) {
    return {
        survey_id: state.documents.survey_id
    }
}

export default connect(mapStateToProps, { submitDocument })(SubmitDocuments);

Также из-за webpack упоминается в ошибке, вот конфиг для нее:

var path = require('path')
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
  context: __dirname,

  entry: [
    '../react/index'  
  ],

  output: {
      path: path.resolve('./src/assets/bundles/'),
      filename: './js/[name]-[hash].js'
  },

  plugins: [
    new BundleTracker({filename: './src/config/webpack-stats.json'}),
    new ExtractTextPlugin({filename: './css/[name].[hash].css', allChunks: true})
  ],

  module: {
    loaders: [
      // {test: /\.(jpe?g|png|gif|svg)$/i, loader: "url-loader?name=img/[name].[ext]"},
      { 
        test: /\.jsx?$/, 
        exclude: /node_modules/, 
        loader: 'babel-loader',
        query: {
          presets: ["react", "es2015", "stage-1"]
        }
      },
      {
        test: /\.json$/,
        loader: ['json-loader'] 
      },
      {
        test: /\.scss$/,
        loader: ['style-loader', 'css-loader', 'sass-loader'] 
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader'
        })      
      },   
    ],
  },

  resolve: {
    extensions: ['*', '.js', '.jsx', '.gif']
  }
}

Во всяком случае, не уверен, что является причиной, и пытался решить эту проблему в течение нескольких дней. В остальном все работает нормально, выглядит просто непрофессионально.

3 ответа

Решение

Создатели Redux Thunk Action возвращают функцию с аргументами (dispatch, getState)и внутри этой функции вы dispatch() действия, когда ваши данные доступны.

Вероятно, ваш код содержит ошибку, потому что вы ничего не вернули создателю действия. Так как это асинхронно, верните внутреннюю функцию:

export function submitDocument(files) {
    return function (dispatch, getState) {
        const uploaders = _.map(files, f => {
            const formData = new FormData();

            formData.append('file', f);

            return axios.post(
                `${ROOT_URL}/api/documents/fileupload`,
                formData,
                { headers:
                    {
                        'content-type': 'multipart/form-data',
                        'Authorization': 'JWT ' +  sessionStorage.getItem('token')
                    }
                }
            )

        });

        axios.
            all(uploaders)
            .then(response => {
                console.log('Success');
                dispatch({ type: DOCUMENTS, payload: ... });
            })
            .catch(error => {
                console.log('Failed');
                dispatch({ type: 'YOUR_ERROR_TYPE', payload: ... });
            })
        };
    }

В моем случае расширение LastPass вызывало эту ошибку, а не мой код (onloadwff.js).
Исправлено, удалив расширение.

Я столкнулся с аналогичной проблемой с redux-toolkit. который, как я выяснил, был вызван неправильным импортом Reducer-Action. Это было вызвано использованием импорта DEFAULT вместо импорта NAMED.

Например

      //Reducer Action export from userSlice
export const { addUser } = userSlice.actions

//Wrong Reducer Action Import
import addUser from '../reducers/userSlice'

//Correct Reducer Action Import
import {addUser} from '../reducers/userSlice'
Другие вопросы по тегам