如何在同一组件中使用react-redux控制加载标志?

时间:2018-05-20 10:34:10

标签: react-native react-redux

我使用react-redux创建了一个项目,我的项目有很多城市,点击每个城市都会导航到我的班级组件TheaterList.js

我设置我的reducer有一个loading标记(当获取数据成功时默认为true然后使用false返回它)我的操作MOVIELIST_THEATER,这是我的MovieListReducer.js代码:

import { 
  MOVIELIST_MAINACTIVITY, 
  MOVIELIST_THISWEEK,
  MOVIELIST_THEATER,
  MOVIELIST_TIME,
  MOVIE_DETAIL
} from '../actions/types';

const INITIAL_STATE = {
  mainMovie: [],
  thisWeek: [],
  theaterList: [],
  timeList: [],
  movieDetail: [],
  loading: true
};

export default (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case MOVIELIST_MAINACTIVITY:
      return {
        ...state,
        mainMovie: action.payload
      };
    case MOVIELIST_THISWEEK:
      return {
         ...state,
         thisWeek: action.payload
      };
    case MOVIELIST_THEATER:
      return {
         ...state,
         theaterList: action.payload,
         loading: false 
      };
    case MOVIELIST_TIME:
      return {
        ...state,
        timeList: action.payload
      };
    case MOVIE_DETAIL:
      return {
        ...state,
        movieDetail: action.payload
      };
    default:
      return state;
  }
};

这是我的班级组件TheaterList.js

import React, { Component } from 'react';
import { View, Text, FlatList } from 'react-native';
import { ListItem } from 'react-native-elements';
import { connect } from 'react-redux';
import { fetchTheater } from '../actions';
import { Spinner } from './common';

class TheaterList extends Component {
  constructor(props) {
    super(props);
    this.renderItem = this.renderItem.bind(this);
    const { enCity } = this.props.navigation.state.params;
    this.state = { 
      navigation: this.props.navigation,
      enCity
    };
  }
  componentDidMount() {
    this.props.fetchTheater({ enCity: this.state.enCity });
  }

  renderItem({ item }) {
    // some view tag
  }

  render() {
    const theaters = this.props.theaterList;
    // the default is true when fetch data succeed then return it with false
    if (this.props.loading) {
      return (
        <Spinner text='Loading...' />
      );
    }
    return (
      <View style={{ flex: 1 }}>
        <FlatList
          data={theaters}
          renderItem={this.renderItem} 
          numColumns={1}
          horizontal={false}
          keyExtractor={(item, index) => index.toString()} 
        />
      </View>
    );
  }
}

const mapStateToProps = (state) => {
  const theaterList = state.listType.theaterList;
  const loading = state.listType.loading;

  return { theaterList, loading };
};

const styles = {
 // some style settings
};

export default connect(mapStateToProps, { fetchTheater })(TheaterList);

一开始效果很好,就像显示<Spinner />等待数据返回并重新呈现状态然后显示我的<FlatList />,但当我回到屏幕并导航到{{1再次,我发现TheaterList.js值仍为this.props.loading。这会导致false无法显示。

我尝试控制加载标记,在<Spinner />函数下添加this.props.loading = true;。显然这是不对的。

这是我的componentDidMount()

action.js

那么,如果使用相同的类组件,如何将import { MOVIELIST_MAINACTIVITY, MOVIELIST_THISWEEK, MOVIELIST_THEATER, MOVIELIST_TIME, MOVIE_DETAIL } from './types'; export const fetchMainMovieList = () => { return (dispatch) => { fetch('https://obscure-reaches-65656.herokuapp.com/api?city=Taipei&theater=Centuryasia') .then(response => response.json()) .then(responseData => { dispatch({ type: MOVIELIST_MAINACTIVITY, payload: responseData[0].movie }); }) .catch((error) => console.log(error)); }; }; export const fetchThisWeek = () => { return (dispatch) => { fetch('https://obscure-reaches-65656.herokuapp.com/api/thisWeek') .then(response => response.json()) .then(responseData => { dispatch({ type: MOVIELIST_THISWEEK, payload: responseData[0].movie }); }) .catch((error) => console.log(error)); }; }; export const fetchTheater = ({ enCity }) => { return (dispatch) => { fetch(`https://obscure-reaches-65656.herokuapp.com/api/drivers?city=${enCity}&lng=121.584065&lat=25.041317`) .then(response => response.json()) .then(responseData => { console.log(responseData); dispatch({ type: MOVIELIST_THEATER, payload: responseData }); }) .catch((error) => console.log(error)); }; }; export const fetchTime = ({ enCity, theater }) => { return (dispatch) => { fetch(`https://obscure-reaches-65656.herokuapp.com/api?city=${enCity}&theater=${theater}`) .then(response => response.json()) .then(responseData => { dispatch({ type: MOVIELIST_TIME, payload: responseData[0].movie }); }) .catch((error) => console.log(error)); }; }; export const fetchDetail = ({ enCity, cnName }) => { return (dispatch) => { fetch(`https://obscure-reaches-65656.herokuapp.com/api/detail?city=${enCity}&movie=${cnName}`) .then(response => response.json()) .then(responseData => { dispatch({ type: MOVIE_DETAIL, payload: responseData[0].movie }); }) .catch((error) => console.log(error)); }; }; 标志与loading完全一致?

任何帮助将不胜感激。提前谢谢。

根据@MRFrhn的建议,我做了一些改变。

在我的react-redux添加action.js之前获取数据:

dispatch({ type: MOVIE_REQUEST_THEATER });

在我的reducer中添加一个案例:

export const fetchDetail = ({ enCity, cnName }) => {
  return (dispatch) => {
    dispatch({ type: MOVIE_REQUEST_THEATER });

    fetch(`https://obscure-reaches-65656.herokuapp.com/api/detail?city=${enCity}&movie=${cnName}`)
      .then(response => response.json())
      .then(responseData => {
        dispatch({ type: MOVIE_DETAIL, payload: responseData[0].movie });
      })
      .catch((error) => console.log(error));    
  };
};

我认为它应该会成功,因为我在获取数据之前返回case MOVIE_REQUEST_THEATER: console.log('MOVIE_REQUEST_THEATER'); return { ...state, loading: true, // show the spinner }; 标记loading,但它没有用,然后我看到我的日志,true没有显示出。不知道为什么......

1 个答案:

答案 0 :(得分:1)

当您第一次获取数据时,loading为真(从初始状态开始),当您收到回复时,将其设置为 false ,但您从未将其设置为true再次,所以下次你读它时会是假的。每次要获取时,都必须将其设置为 true 。您可能还需要处理错误,例如当您的请求失败时,因此在redux中以正确的方式实现此错误,您需要针对您的请求执行3个操作:

  1. 通知减压器请求开始的操作。 Reducer可以通过在状态中切换isFetching标志(或代码中的loading)来处理此操作。这样UI就知道是时候展示一个微调器了。例如操作类型MOVIELIST_THEATER_REQUEST
  2. 通知减压器请求已成功完成的操作。 Reducer可以通过将新数据合并到他们管理的状态并重置isFetching来处理此操作。 UI将隐藏微调器,并显示获取的数据。例如操作类型MOVIELIST_THEATER_SUCCESS
  3. 通知减压器请求失败的操作。减压器可以通过重置isFetching来处理此操作。此外,某些Reducer可能希望存储错误消息,以便UI可以显示它。例如操作类型MOVIELIST_THEATER_FAILURE
  4. 所以你的减速器看起来像这样:

    case MOVIELIST_THEATER_REQUEST:
      return {
         ...state,
         loading: true, // this will show the spinner
         error: false
    };
    
    case MOVIELIST_THEATER_SUCCESS:
      return {
         ...state,
         theaterList: action.payload,
         loading: false,
         error: false
    };
    
    case MOVIELIST_THEATER_FAILURE:
      return {
         ...state,
         loading: false,
         error: true
    };
    
    ...
    

    阅读this了解详情。

    ============

    更新

    将您的操作更改为:

    export const fetchDetail = ({ enCity, cnName }) => {
       return (dispatch) => {
          dispatch(request_theater());
    
           fetch(`https://obscure-reaches-65656.herokuapp.com/api/detail?city=${enCity}&movie=${cnName}`)
            .then(response => response.json())
            .then(responseData => {
                dispatch({ type: MOVIE_DETAIL, payload: responseData[0].movie });
                })
            .catch((error) => console.log(error));    
       };
    };
    
    export function request_theater() {
        return {
            type: MOVIE_REQUEST_THEATER
        }
    }