我原本以为这是react-redux,axios,lodash和/或react-dropzone的问题;但是,文件上传正常,但仍会触发此错误. (即使在控制台中显示“失败”,文件也会被发布到服务器). 我开始认为这个问题可能与
我开始认为这个问题可能与react-router-redux有关,基于我在这里读到的内容:
https://github.com/reactjs/react-router-redux/issues/182
但是,这些建议对我不起作用.
无论如何,我得到一个未捕获的TypeError:无法读取未定义的属性’type’我无法确定是什么原因导致它或如何解决它.以下是错误的一些屏幕截图:
所以这里应该是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'] } }
无论如何,不知道是什么导致它,并且已经尝试解决它几天了.一切都运行正常,否则,它看起来不专业.
Redux thunk个动作创建者返回带有参数(dispatch,getState)的函数,并且在该函数内部,当您的数据可用时,您可以调度()动作.您的代码可能会出错,因为您根本没有从动作创建者那里返回任何内容.由于它是异步的,返回内部函数:
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: ... }); }) }; }