"Uncaught TypeError: Cannot read property 'type' of undefined" and unclear what it is related to

29,689

Solution 1

Redux thunk action creators return a function with the arguments (dispatch, getState), and inside that function, you dispatch() actions when your data is available.

Your code is likely erroring because you haven't returned anything from your action creator at all. Since it's async, return the inner function:

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: ... });
            })
        };
    }

Solution 2

In my case, LastPass extension was causing this error and not my code (onloadwff.js).
Fixed it by removing the extension.


As per lastpass-cli/issues/428#comment, you can avoid this by updating the LastPass extension settings to only run when clicked.

Solution 3

I was facing similar issue with redux-toolkit. which I found out was being caused by wrong Reducer-Action import. It was caused by using DEFAULT import where it should have been a NAMED import.

For example

//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'
Share:
29,689
cjones
Author by

cjones

Updated on July 05, 2022

Comments

  • cjones
    cjones almost 2 years

    I originally thought this was an issue with react-redux, axios, lodash, and/or react-dropzone; however, files are uploading fine, but this error still triggers. (Even though it is says "failed" in console, the files are being posted to the server).

    I am starting to think the issue might be related to react-router-redux based on what I read here:

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

    However, the suggestions are not working for me.

    Anyway, I am getting an Uncaught TypeError: Cannot read property 'type' of undefined that I can't really tell what is causing it or how to resolve it. Here are some screenshots of the errors:

    undefined line

    So here is what should be relevant to react-router. Just not sure what is going on.

    The entry point:

    // ./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'));
    

    The global reducers and those that pertain to the action that triggers the error.

    // ./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;
    }
    

    Finally, the action that is being called when the error is raised. One important thing to point out: the error does not occur when the the axios.post is not in _.map (actually any kind of array iteration). However, that only sends one file which is not the behavior I am after.

    // ./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');
            })
    }
    

    The container that calls the action would probably be helpful as well:

    // ./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);
    

    Also, because webpack is mentioned in the error, here is the config for it:

    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']
      }
    }
    

    Anyway, not sure what is causing it and have been trying to resolve it for several days now. Everything works fine otherwise, it just looks unprofessional.