简体   繁体   中英

React-Redux Store is not properly changed by reducer

I'm struggling to learn React-Redux. I'm making a simple application that fetch data from Teleport API and show the list according to the user's input.

My issue is that even though action is dispatched in container component, state is not changed and the result does not show up.

This is a screenshot of console after dispatching action twice.

在此处输入图片说明

I suppose that there is an issue to store data properly. I appreciate if you can help me.

Here is my code.

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

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

You can use redux api middleware to do the api call.All you have to change is

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'))

Note: you can install Redux devtools extension in chrome and u can check the redux store in your chrome developer tools.I think this will be easy to find out what happens in your redux store.

Since axios.get(url) is asynchronous you need to wait for the request to succeed and then then dispatch an action. You could make use of middlewares such as redux-thunk while creating a store and then do the following

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM