Как вызвать mapStateToProps для каждого экземпляра компонента в React-Redux - PullRequest
0 голосов
/ 29 июня 2018

У меня есть компонент с именем AuditionItem, несколько экземпляров которого добавляются в родительский компонент с именем AuditionsList.

Я сделал экспорт по умолчанию подключиться (mapStateToProps) (AuditionItem)

Из моего опыта mapStateToProps вызывается только для одного экземпляра AuditionItem (тот, который инициирует изменение состояния). Но я хочу, чтобы mapStateToProps вызывался для КАЖДОГО экземпляра AuditionItem.

Есть ли способ сделать это?

Вот мой код для AuditionItem.js:

import React from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Moment from 'moment';
import colors from './../styles/colors';
import { store } from './../App';
import { addBookmark, removeBookmark } from './../actions/creators';
import { connect } from 'react-redux';

class AuditionItem extends React.Component {

  _toggleBookmark = (auditionId, bookmarked) => {
    if(bookmarked)
      store.dispatch(removeBookmark(auditionId));
    else
      store.dispatch(addBookmark(auditionId));
  }

  render() {
    Moment.locale('en');

    let bookmarked = (this.props.auditions.indexOf(this.props.auditionId) > -1) ? true : false;

    let roleString = String(this.props.role);
    if(roleString.length > 35)
      roleString = roleString.substring(0, 35) + " ...";
    let projectString = String("Project: (" + this.props.productionType + ") " + this.props.project);
    if(projectString.length > 35)
      projectString = projectString.substring(0, 35) + " ...";
    let productionHouseString = String("Production House: " + this.props.productionHouse);
    if(productionHouseString.length > 35)
      productionHouseString = productionHouseString.substring(0, 35) + " ...";
    let iconName = `ios-bookmark${bookmarked ? '' : '-outline'}`;

    return (
      <View style={styles.auditionItemWithBookmark}>
        <View style={styles.bookmark}>
          <TouchableOpacity onPress={() => this._toggleBookmark(this.props.auditionId, bookmarked)} >
            <Ionicons name={iconName} size={25} />
          </TouchableOpacity>
       </View>
        <View style={styles.auditionItem}>
          <Text style={styles.role}>{roleString}</Text>
          <Text style={styles.project}>{projectString}</Text>
          <Text style={styles.productionHouse}>{productionHouseString}</Text>
          <Text style={styles.auditionDate}>Begins: {Moment(String(this.props.auditionDate).replace('"','').replace('"', '')).format('LLLL')}</Text>
        </View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  auditionItemWithBookmark: {
    flex: 1,
    flexDirection: "row",
    backgroundColor: colors.auditionItemBackgroundColor,
    borderRadius: 10,
    margin: 10,
    padding: 15,
  },
  bookmark: {
    flex: 1,
    paddingTop: 5,
  },
  auditionItem: {
    flex: 8,
    flexDirection: "column",
    backgroundColor: colors.auditionItemBackgroundColor,
  },
  role: { color: colors.auditionItemColor, fontSize: 20, fontWeight: "bold" },
  project: { color: colors.auditionItemColor },
  productionHouse: { color: colors.auditionItemColor },
  auditionDate: { color: colors.auditionItemColor },
});

const mapStateToProps = state => {
  return {
    auditons: state.bookmarks.auditions,
  }
}

export default connect(mapStateToProps)(AuditionItem);

И код для родительского AuditionsList.js

import React from 'react';
import { Text, View, FlatList, ActivityIndicator } from 'react-native';
import { connect } from 'react-redux';
import AuditionItem from './AuditionItem';
import Auditions from './../data/Auditions';
import { store } from './../App';

class AuditionsList extends React.Component {

    constructor(props) {
      super(props);
      this.state = { isLoading: true, data: []  }
    }

    componentDidMount() {
      this._refreshData();
    }

    componentDidUpdate(prevProps) {
      if((this.props.location !== prevProps.location) || (this.props.roleType !== prevProps.roleType))
        this._refreshData();
    }

    _onRefresh() {
      this.setState({ isLoading: true }, this._refreshData() );
    }

    _refreshData = () => {
        Auditions.fetchAuditions(this.props.productionType, this.props.location, this.props.roleType).then(auditions => {
          this.setState({ isLoading: false, data: this._addKeysToAuditions(auditions) });
        });
    }

    _addKeysToAuditions = auditions => {
      return auditions.map(audition => {
          return Object.assign(audition, { key: audition.Role});
      });
    }

    _renderItem = ({ item }) => {
      return (
        <AuditionItem
          auditionId={item.objectId}
          role={item.Role}
          project={item.Project.Name}
          productionType={item.Project.ProductionType.Type}
          auditionDate={JSON.stringify(item.Date.iso)}
          productionHouse={item.Project.ProductionHouse.Name}
          auditions={store.getState().bookmarks.auditions}
        />
      );
    }

    render() {
      if (this.state.isLoading) {
        return (
          <View style={{flex: 1, paddingTop: 20}}>
            <ActivityIndicator />
          </View>
        );
      }

      return (
        <View style={{ flex: 1 }}>
          <FlatList onRefresh={() => this._onRefresh()} refreshing={this.state.isLoading} data={this.state.data} renderItem={this._renderItem} />
       </View>
      );
    }

}

const mapStateToProps = state => {
  return {
    location: state.settings.location,
    roleType: state.settings.roleType,
  };
}

export default connect(mapStateToProps)(AuditionsList);

Код для App.js:

import React from 'react';
import { Text, View, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Header } from 'react-native-elements';
import { createMaterialBottomTabNavigator } from 'react-navigation-material-bottom-tabs';
import { SettingsDividerShort, SettingsCategoryHeader, SettingsPicker} from 'react-native-settings-components';
import BookmarksScreen from './screens/BookmarksScreen';
import AuditionsScreen from './screens/AuditionsScreen';
import SettingsScreen from './screens/SettingsScreen';

import { AsyncStorage } from "react-native";
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import rootReducer from './reducers/index';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import { autoMergeLevel2 } from 'redux-persist/lib/stateReconciler/autoMergeLevel2';

const persistConfig = {
 key: 'root',
 storage: AsyncStorage,
 stateReconciler: autoMergeLevel2,
 whitelist: ['settings', 'bookmarks']
};

const pReducer = persistReducer(persistConfig, rootReducer);

export const store = createStore(pReducer);
export const persistor = persistStore(store);

const MaterialBottomTabNavigator = createMaterialBottomTabNavigator(
  {
    Bookmarks: BookmarksScreen,
    Auditions: AuditionsScreen,
    Settings: SettingsScreen,
  },
  {
    shifting: true,
    initialRouteName: 'Auditions',
    barStyle: {  backgroundColor: 'black' },
  }
);

export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <PersistGate loading={<ActivityIndicator />} persistor={persistor}>
          <MaterialBottomTabNavigator />
        </PersistGate>
      </Provider>
    )
  }
}

1 Ответ

0 голосов
/ 29 июня 2018

Это не создает экземпляр класса , он просто экспортирует класс / объект.

export default connect(mapStateToProps)(AuditionItem)

Экспортная ссылка

Вы получаете экземпляр, когда вызываете конструктор класса, но это после его импорта. Таким образом, в основном, все время, когда вы используете импортированный AuditionItem (т.е. ), React внутренне создает новый экземпляр класса.

Полагаю, проблема либо в самом AuditionItem, либо в реквизите, на который вы переходите.

Дополнительная информация о mapStateToProps из официальной документации Redux

[mapStateToProps (state, [ownProps]): stateProps] (Function): если указан этот аргумент, новый компонент будет подписываться на обновления хранилища Redux. Это означает, что каждый раз, когда хранилище обновляется, вызывается mapStateToProps. Результатом mapStateToProps должен быть простой объект, который будет объединен с реквизитом компонента. Если вы не хотите подписываться на обновления магазина, передайте значение null или undefined вместо mapStateToProps.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...