【问题标题】:React-Redux Store is not properly changed by reducerReact-Redux Store 没有被reducer 正确改变
【发布时间】:2018-04-27 05:17:09
【问题描述】:

我正在努力学习 React-Redux。我正在制作一个简单的应用程序,它从Teleport API 获取数据并根据用户的输入显示列表。

我的问题是,即使在容器组件中调度了动作,状态也没有改变,结果也没有显示出来。

这是两次 dispatch action 后的控制台截图。

我认为正确存储数据存在问题。如果您能帮助我,我将不胜感激。

这是我的代码。

/container/search.js

class Search extends Component{
 constructor(props){
    super(props);
    this.state = {
      city : ""
    }
   this.handleSubmit = this.handleSubmit.bind(this);
   this.handleChange = this.handleChange.bind(this);
   }

handleChange(event) {
    console.log(event.target.value)
    this.setState({
        city: event.target.value
    });
}

handleSubmit(event){
    event.preventDefault();
    console.log(this.state.city)
    this.props.addData(this.state.city);
    this.setState({
        city: ""
    });
}
render(){
    return(
        <div>
        <form onSubmit={this.handleSubmit}>
        <input type="text"
               placeholder="add city name"
               onChange={this.handleChange}
               value={this.state.city} />
        <button>Submit</button>
        </form>
        </div>
        )
}
}

function mapDispatchToProps(dispatch) {
return bindActionCreators({ addData }, dispatch);
}

export default connect(null, mapDispatchToProps)(Search);

/actions/index.js

import axios from 'axios';

const ROOT_URL = "https://api.teleport.org/api/cities/?search";

const ADD_DATA = 'ADD_DATA';

export function addData(city){
    const url = `${ROOT_URL}=${city}`;
    const request = axios.get(url);
    return {
        type: ADD_DATA,
        payload: request
    };
 }

/reducers/reducer_data.js

import { ADD_DATA } from "../actions/index";

export default function( state=[], action) {
switch(action.type) {
    case ADD_DATA:
        return [action.payload.data, ...state];
}
return state;
}

/reducers/index.js

import { ADD_DATA } from "../actions/index";

export default function( state=[], action) {
    switch(action.type) {
        case ADD_DATA:
            return [action.payload.data, ...state];
}
return state;
}

//编辑 // index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import logger from 'redux-logger'

import reducers from './reducers';
import Search from './containers/search';

const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, logger)(createStore);

ReactDOM.render(
    <Provider store={createStoreWithMiddleware(reducers)}>
    <Search />
    </Provider>
, document.querySelector('.container'));

【问题讨论】:

  • axios.get(url) 是异步的。发送之前不需要等待吗?
  • 我使用redux-promise 进行异步请求。我会更新我的代码。
  • 你可能需要使用一些像 redux-thunk 这样的中间件来在 redux 逻辑中加入 axios/fetches,请阅读:stackoverflow.com/questions/34570758/…

标签: javascript reactjs redux react-redux


【解决方案1】:

你可以使用 redux api 中间件来做 api 调用。你需要改变的是

actions/index.js

import {CALL_API} from 'redux-api-middleware';


export const addData=(city)=>({

    [CALL_API]:{

        endpoint:"https://api.teleport.org/api/cities",
        query:{
              "search":city
             }
        method:'GET',
        types:["ADD_DATA","ADD_DATA_SUCCESS","ADD_DATA_FAILURE"]
    }
    });

/reducers/reducer_data.js

           import {combineReducers} from 'redux'
                const InitialData={
                    dataList:[],
                    error:null,
                    loading:false
                }
                export const dataList=(state=InitialData,action)=>{
                    switch(action.type){
                        case "ADD_DATA":
                            return Object.assign({},state,{
                                dataList:[],error:null,loading:true
                            })
                        case "ADD_DATA_SUCCESS":
                            return Object.assign({},state,{
                                dataList:action.payload,error:null,loading:false
                            })
                        case "ADD_DATA_FAILURE":
                           error = action.payload.data || { message: action.payload };
                            return Object.assign({},state,{
                                dataList:action.payload,error:error,loading:false
                            })
                        default:
                            return state;
                    }
                }

            //Its better to use combine reducer to combine all the reducers 
            //you have in your app  as one as redux permits only one store per app  


            export const reducers=combineReducers({
            DataList:dataList
            })

export default reducers

store.js

import {
  applyMiddleware,
  createStore,compose
} from 'redux';

// import thunk from 'redux-thunk';
import { apiMiddleware } from 'redux-api-middleware';
import {CALL_API} from 'redux-api-middleware';
import promise from 'redux-promise';
import reducers from './reducer';
import { logger} from 'redux-logger'; 
import ReduxThunk from 'redux-thunk' 

import qs from 'querystring'



 function queryMiddleware() {
  return next => action => {
    if (action.hasOwnProperty(CALL_API) && action[CALL_API].hasOwnProperty('query')) {
      const request = action[CALL_API];
      request.endpoint = [
        request.endpoint.replace(/\?*/, ''),
        qs.stringify(request.query),
      ].join('?');
      delete request.query;

      return next({ [CALL_API]: request });
    }

    return next(action);
  };
}
export function ConfigureStore(IntitialState={}){
    const stores=createStore(reducers,IntitialState,compose(
        applyMiddleware(queryMiddleware,ReduxThunk,apiMiddleware,promise),
         window.devToolsExtension ? window.devToolsExtension() : f => f
        ));


    return stores;
}; 
 export const  store=ConfigureStore()

index.js

import React from 'react'
import  ReactDOM from 'react-dom'
import store from './store'
import Search from './Search'
ReactDOM.render((
     <Provider store={store}>
            <Search />
        </Provider>
    ),document.getElementById('main-container'))

注意:你可以在 chrome 中安装 Redux devtools 扩展,你可以在你的 chrome 开发者工具中查看 redux 商店。我认为这将很容易找出你的 redux 商店中发生的事情。

【讨论】:

    【解决方案2】:

    由于 axios.get(url) 是异步的,您需要等待请求成功,然后再分派一个操作。您可以在创建商店时使用redux-thunk 等中间件,然后执行以下操作

    /actions/index.js

    const ADD_DATA = 'ADD_DATA';
    
    export function addData(city){
    
        return function(dispatch) {
             const url = `${ROOT_URL}=${city}`;
             axios.get(url)
                   .then((res) => {
                        dispatch({
                           type: ADD_DATA,
                           payload: res.data
                        });
                   });
        }
    
     }
    

    /reducers/index.js

    import { ADD_DATA } from "../actions/index";
    
    export default function( state=[], action) {
        switch(action.type) {
            case ADD_DATA:
                return [...state, action.payload];
    }
    return state;
    }
    

    【讨论】:

    • 我试过你的答案,它显示一个错误:Error: Actions must be plain objects. Use custom middleware for async actions.。我会使用 redux-thunk 并告诉你。
    • 我添加了 redux-thunk 并尝试了。动作前后的状态仍然是空的......
    猜你喜欢
    • 2019-12-05
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-20
    • 2019-05-16
    • 2018-07-26
    • 2022-01-13
    • 1970-01-01
    相关资源
    最近更新 更多