首页 > 解决方案 > 从类外调用时,store.dispatch 不更新道具

问题描述

store.js

import AsyncStorage from "@react-native-community/async-storage";
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import { createLogger } from "redux-logger";

import thunk from "redux-thunk";

import reducers from "../reducers";

const logger = createLogger({
  // ...options
});

const persistedReducer = persistReducer(persistConfig, reducers);

 export default () => {

  let store = createStore(persistedReducer, {}, applyMiddleware(thunk, logger));
  let persistor = persistStore(store);
  return { store, persistor };
};

PathexploredTab.js

import * as React from "react";
import {
  View,
  StyleSheet,
  Text,
  Dimensions,
  TouchableOpacity,
  Alert,
  Platform,
  Image
} from "react-native";
import { TabView, SceneMap, TabBar } from "react-native-tab-view";
import { Config } from "@common";
import { connect } from "react-redux";
import { compose } from "redux";
import { Dropdown } from "react-native-material-dropdown";
import { Field, reduxForm } from "redux-form";
import { ScrollView } from "react-native-gesture-handler";
import { Actions } from "react-native-router-flux";
import Icon from "react-native-vector-icons/Feather";
import {
  searchImmipaths,
  isKeepData,
  backToInitialState,
  continueFromPrevious,
  fetchDynamicFacts,
  pathExplorerTutorial
} from "../actions/path.actions";
import { checkConnection } from "../service/checkConnection";
import Loader from "./Loader";
import { colors, normalize, family, Images } from "@common";
import ResponsiveImage from 'react-native-responsive-image';
import { RFValue } from "react-native-responsive-fontsize";
import RNPicker from "rn-modal-picker";
import * as Animatable from 'react-native-animatable';
import UserinfoPopUp from "./../pages/UserinfoPopUp";
import { updateUserDetails, getUserDetails } from "../actions/auth.actions";
import AsyncStorage from "@react-native-community/async-storage";
import { copilot, walkthroughable, CopilotStep } from 'react-native-copilot';
import { StepNumberComponent } from "./../pages/newProfileScreen";
import persist from "./../config/store";
import Reactotron from 'reactotron-react-native'

const WalkthroughableImage = walkthroughable(Image);
const WalkthroughableText = walkthroughable(Text);
const WalkthroughableView = walkthroughable(View);
const WalkthroughableTouch = walkthroughable(TouchableOpacity);
const persistStore = persist();
const TooltipComponent = ({
  isFirstStep,
  isLastStep,
  handleNext,
  handlePrev,
  handleStop,
  labels,
  currentStep,
}) => {
  const handleDiscardTutorial = () => {
      Alert.alert(
          '',
          'are you sure you don’t want a tutorial on how to use the app?',
          [
            {
              text: 'yes',
              onPress: () => {AsyncStorage.setItem('DontShowTutorial', JSON.stringify(true)), handleStop()}
            },
            {
              text: 'Cancel',
              onPress: () => console.log('Cancel Pressed'),
              style: 'cancel'
            },
          ],
          { cancelable: false }
        );
  }
  return (
      <View style={styles.tooltipContainer}>
          <Text testID="stepDescription" style={styles.tooltipText}>{currentStep.text}</Text>
          <View style={[styles.bottomBar]}>
              {
                  !isFirstStep ?
                      <TouchableOpacity style={styles.toolTipButton} onPress={handlePrev}>
                          <Text style={styles.toolTipButtonText}>{'Previous'}</Text>
                      </TouchableOpacity>
                      : null
              }
              {
                  !isLastStep ?
                      <TouchableOpacity style={styles.toolTipButton} onPress={handleNext}>
                          <Text style={styles.toolTipButtonText}>{'Next'}</Text>
                      </TouchableOpacity> :
                      <TouchableOpacity style={styles.toolTipButton} onPress={handleStop}>
                          <Text style={styles.toolTipButtonText}>{'Finish'}</Text>
                      </TouchableOpacity>
              }
               <TouchableOpacity onPress={() => persistStore.store.dispatch(pathExplorerTutorial('bbv'))}>
                  <Text style={styles.toolTipButtonText}>Go</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={handleDiscardTutorial}>
                  <Text style={styles.toolTipButtonText}>Do not show tutorial</Text>
              </TouchableOpacity>
          </View>
      </View>
  );
}
const window = Dimensions.get("window");
var width = window.width;
var height = window.height;
var immigrationInterst = [];
var countryIntrest = [];
let countryIntrestNow = [];
var FirstRoute = () => <View style={[{ height: height }]} />;

let disableButton = false;
var lastSearchedCountries = [];
var SecondRoute = () => <View style={[{ height: height }]} />;

class PathexploredTab extends React.Component {
  constructor(props) {
    super(props);
    this.imageMap = ['', Images.studyImage, Images.workImage, Images.residencyImage, Images.tourismImage];
    this.state = {
      isloading: false,
      buttonone: false,
      immibut1: false,
      immibut2: false,
      immibut3: false,
      immibut4: false,
      userInfoPopUpVisible: false, 
      countrybut1: false,
      countrybut2: false,
      countrybut3: false,
      countrybut4: false,
      filedsToShow: [],
      countrybut5: false,
      countrybut6: false,
      selectedval: "Select the Country",
      index: 0,
      routes: [
        { key: "first", title: "Select Goals" },
        { key: "second", title: "Select Countries" }
      ],
      checkconnection: false,
      errorMessage: "",
      isItemSelected: true,
      currentlySelectedItemIndex: -1,
      isItemChecked: true,
      checkboxData: ""
    };
  }
  componentDidUpdate = (nextProps) => {
    Reactotron.log('=====', nextProps.userPressedGo)
  }
  componentWillReceiveProps = (nextProps) => {
    

  }
  componentWillMount = async() => {
    const {
      getUser: { userDetails },
      authData: { token }
    } = this.props;
    this.props.dispatch(getUserDetails(userDetails.userid, token))
  }
  componentDidMount() {
    setTimeout(() => {
       this.props.start();
    }, 1000);
    immigrationInterst = [];
    countryIntrest = [];
    countryIntrestNow = [];
    const {
      getUser: { userDetails }
    } = this.props;
   
  
  }


  
  callProceed = () => {
     const {
      isItemSelected,
      currentlySelectedItemIndex,
      isItemChecked
    } = this.state;
    if (isItemSelected) {
      Alert.alert("Immigreat", "Please select any goal");
      return;
    }
    const lastGoal = this.props.immigationInterest[0]

    //This is a bit of a hacky fix but it works.
    let allPrevCountriesFound = lastSearchedCountries.every(ai => countryIntrestNow.includes(ai))
      && (lastSearchedCountries.length == countryIntrestNow.length);

    //We want to clear and search when:
    //a) There is no exploration id
    //b) Even if there is an exploration id but the lastGoal or last countries dont match
    //Note: We make one exception for if exploration id exists but the lastSearchedCountries is just an empty list

    let noExplorationId = (this.props.explorationId === "");
    let countryGoalDontMatch = (lastGoal != currentlySelectedItemIndex || !allPrevCountriesFound);
    let isProceedException = (lastSearchedCountries.length == 0 && !noExplorationId);

    noExplorationId || (!noExplorationId && countryGoalDontMatch && !isProceedException)
      ?
      this.clearAndSearch()
      : Alert.alert(
        "Immigreat",
        "Previous exploration found. Would you like to continue?",

        [
          {
            text: "START FROM BEGINNING",
            onPress: () => {
              this.props.dispatch(continueFromPrevious(0));
              this.props.dispatch(backToInitialState());
              this.props.dispatch(isKeepData(false));
              this._searchData();
            },
            style: "cancel"
          },
          {
            text: "CONTINUE",
            onPress: () => {
              lastSearchedCountries = [...countryIntrest];
              this.props.dispatch(isKeepData(true));
              this.props.didTapOnSearch();
            }
          },
          {
            text: "Cancel",
            onPress: () => console.log("Canceled"),
            style: "cancel"
          }
        ],
        { cancelable: false }
      );
  }
 
  
 
  _onPressBackButton = () => {
    this.setState(
      {
        isItemSelected: true,
        currentlySelectedItemIndex: -1,
        isItemChecked: false
      },
      () => {
        setTimeout(() => {
          this.intialLoadValues();
          //this.setState({ isItemSelected: true });
        }, 0);
      }
    );
  }

  extraOptionsRefresh() {
    const { currentlySelectedItemIndex, isItemChecked } = this.state;
    this.setState({ isItemChecked: !isItemChecked })
    switch (currentlySelectedItemIndex) {
      case 1:
        this.setState({checkBoxData:
          "Do you want to explore options you may have after your studies?"});
        break;
      case 2:
        this.setState({checkBoxData: 
          "Do you want to explore other goal options (such as studies) that can lead to work options?"});
        break;
      case 3:
        this.setState({checkBoxData:
          "Do you want to explore other goal options (such as studies or work) that could lead to permanent residency in the future?"});
        break;
      case -1:
        this.setState({checkBoxData: ""});
        break;
    }
  }

  intialLoadValues() {
    const { currentlySelectedItemIndex, isItemChecked } = this.state;
    /*var checkBoxData = "";
    switch (currentlySelectedItemIndex) {
      case 1:
        checkBoxData =
          "Do you want to explore options you may have after your studies?";
        break;
      case 2:
        checkBoxData = "Do you want to explore other goal options (such as studies) that can lead to work options?";
        break;
      case 3:
        checkBoxData =
          "Do you want to explore other goal options (such as studies or work) that could lead to permanent residency in the future?";
        break;
      case 4:
        checkBoxData = "";
        break;
    }*/

    FirstRoute = () => (
      <ScrollView style={{ flex: 1, backgroundColor: "white" }}>
        <Animatable.View animation="fadeIn" duration={1200} style={{ flex: 1 }}>
          <View
            style={{
              backgroundColor: "#DBDDDF",
              alignItems: "center",
              justifyContent: "center",
              flex: 1
            }}
          >
            <View style={{ padding: 20 }}>
              <Text
                style={{
                  textAlign: "center",
                  fontFamily: "SourceSansPro-Semibold",
                  color: "#2C393F",
                  fontSize: RFValue(12)
                }}
              >
                Select the Immigration goal that you would be interested in
                exploring
              </Text>
              <Text
                style={{
                  textAlign: "center",
                  color: "#008BC7",
                  fontFamily: "SourceSansPro-Regular",
                  fontSize: RFValue(10)
                }}
              >
                You can select/deselect ONLY one goal at a time by clicking the buttons. This is so we can help you hone your search!
              </Text>
            </View>
          </View>

          {(this.state.isItemSelected && this.state.currentlySelectedItemIndex < 0) ? (
            <View style={{ marginTop: 30 }}>
             {/*<Animatable.View animation="fadeIn" duration={1200} style={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>*/}
              <View style={styles.custombuttonView}>
                <View style={{ width: "50%" }}>
                <CopilotStep text="Select Your goals" order={1} name="study">
                  <WalkthroughableTouch
                    testID={`${Config.immigerat[0].code + 'button'}`}
                    onPress={() =>
                      this._immigreateIntrest(Config.immigerat[0].code)
                    }
                    style={
                      this.state.immibut1
                        ? styles.selectedbutton
                        : styles.buttonstyleview
                    }
                  >
                    <WalkthroughableText
                      style={
                        this.state.immibut1
                          ? styles.selectedbuttontext
                          : styles.buttonstyletext
                      }
                    >
                      {Config.immigerat[0].value}
                    </WalkthroughableText>
                  </WalkthroughableTouch>
                  </CopilotStep>
                </View>
                <View style={{ width: "50%" }}>
                  <TouchableOpacity
                    testID={`${Config.immigerat[1].code + 'button'}`}
                    onPress={() =>
                      this._immigreateIntrest(Config.immigerat[1].code)
                    }
                    style={
                      this.state.immibut2
                        ? styles.selectedbutton
                        : styles.buttonstyleview
                    }
                  >
                    <Text
                      style={
                        this.state.immibut2
                          ? styles.selectedbuttontext
                          : styles.buttonstyletext
                      }
                    >
                      {Config.immigerat[1].value}
                    </Text>
                  </TouchableOpacity>
                </View>
              </View>
              <View style={styles.custombuttonView}>
                <View style={{ width: "50%" }}>
                  <TouchableOpacity
                    testID={`${Config.immigerat[2].code + 'button'}`}
                    onPress={() =>
                      this._immigreateIntrest(Config.immigerat[2].code)
                    }
                    style={
                      this.state.immibut3
                        ? styles.selectedbutton
                        : styles.buttonstyleview
                    }
                  >
                    <Text
                      style={
                        this.state.immibut3
                          ? styles.selectedbuttontext
                          : styles.buttonstyletext
                      }
                    >
                      {Config.immigerat[2].value}
                    </Text>
                  </TouchableOpacity>
                </View>
                <View style={{ width: "50%" }}>
                  <TouchableOpacity
                    testID={`${Config.immigerat[3].code + 'button'}`}
                    onPress={() =>
                      this._immigreateIntrest(Config.immigerat[3].code)
                    }
                    style={
                      this.state.immibut4
                        ? styles.selectedbutton
                        : styles.buttonstyleview
                    }
                  >
                    <Text
                      style={
                        this.state.immibut4
                          ? styles.selectedbuttontext
                          : styles.buttonstyletext
                      }
                    >
                      {Config.immigerat[3].value}
                    </Text>
                  </TouchableOpacity>
                </View>
              </View>
              {/*</Animatable.View>*/}
            </View>
          ) : (
              <View style={{ flex: 0.75, marginTop: 30 }}>
              {/*<Animatable.View animation="fadeIn" duration={1200}>*/}
                <View style={{ flexDirection: 'row', alignContent: 'center' }}>
                  <TouchableOpacity testID='goBackButton'
                    onPress={this._onPressBackButton} style={{ padding: 10 }} >
                    <View style={{ width: 80 }}>
                      <Icon name="chevron-left" size={30} color="black" />
                    </View>
                  </TouchableOpacity>
                  {<TouchableOpacity
                    onPress={() => this._immigreateIntrest("null")}
                    style={[styles.selectedbutton, { width: "50%" }]}
                  >
                     <Text style={styles.selectedbuttontext}>
                      {Config.immigerat[this.state.currentlySelectedItemIndex - 1].value}
                    </Text>
                  </TouchableOpacity>}
                </View>
                {currentlySelectedItemIndex !== 4 && (
                  <View
                    style={{
                      borderWidth: 1,
                      borderRadius: 3,
                      borderColor: "rgba(110,110,110,0.4)",
                      flex: 1,
                      marginHorizontal: 10,
                      marginTop: 30,
                      flexDirection: "row"
                    }}
                  >
                    <TouchableOpacity
                      activeOpacity={0.8}
                      onPress={() => {
                          this.extraOptionsRefresh()
                        //setTimeout(() => {
                          //this.extraOptionsRefresh();
                          //this.intialLoadValues();
                          //this.setState({ isItemChecked: !isItemChecked });
                          //}, 100);
                        }
                      }
                      style={{
                        // flex: 0.35,
                        alignItems: "center",
                        justifyContent: "center",
                        marginLeft: 15
                      }}
                      hitSlop={{ top: 20, bottom: 20, left: 50, right: 50 }}
                    >
                      <View
                        style={[
                          styles.button,
                          {
                            backgroundColor: "white",
                            borderColor: "rgba(110,110,110,0.4)",
                            borderWidth: 1,
                            borderRadius: 2,
                            width: 20,
                            height: 20
                          }
                        ]}
                      >
                        {this.state.isItemChecked ? (
                          <Icon name={"check"} color={colors.LIGHT_BLUE} size={15} />
                        ) : null}
                      </View>
                    </TouchableOpacity>
                    <Text
                      style={{
                        flex: 1,
                        marginVertical: 10,
                        marginLeft: 10,
                        fontFamily: "SourceSansPro-Bold",
                        color: "#242424",
                        fontSize: RFValue(12)
                      }}
                    >
                      {this.state.checkBoxData}
                    </Text>
                  </View>
                )}
                {(!this.state.isItemSelected && this.state.currentlySelectedItemIndex > 0) &&
                  <Animatable.View animation="fadeIn" duration={1200} style={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
                    <ResponsiveImage source={this.imageMap[this.state.currentlySelectedItemIndex]} initWidth="270" initHeight="220" />
                  </Animatable.View>
                }
              </View>
            )}
          <View>
            <TouchableOpacity
              testID='searchButton'
              onPress={() => !disableButton && this.showAlert1()}
              style={{
                width: 130,
                justifyContent: "center",
                backgroundColor: colors.LIGHT_BLUE,
                borderRadius: 100,
                padding: 10,
                alignSelf: "center",
                marginTop: height > 700 ? 60 : 40,
                marginBottom: 20
              }}
            >
              <Text
                style={{
                  textAlign: "center",
                  color: "#fff",
                  fontFamily: "SourceSansPro-Regular"
                }}
              >
                Search
              </Text>
            </TouchableOpacity>
          </View>
        </Animatable.View>
      </ScrollView>
    );

    SecondRoute = () => (
      <ScrollView style={{ flex: 1, backgroundColor: "white" }}>
        <View style={{ backgroundColor: "white" }}>
          <View
            style={{
              alignSelf: "center",
              justifyContent: "center",
              width: width
            }}
          >
            <View style={{ margin: 20, alignSelf: "center" }}>
              <Text
                style={{
                  textAlign: "center",
                  fontFamily: "SourceSansPro-Semibold",
                  color: "#2C393F",
                  fontSize: 15
                }}
              >
                Select all countries you are keen to explore
              </Text>
              <Text
                style={{
                  textAlign: "center",
                  color: "rgba(44, 57, 63,0.6)",
                  marginTop: 20,
                  fontFamily: "SourceSansPro-Semibold"
                }}
              >
                From:
              </Text>
              <View style={styles.loginView}>
                <Field
                  name="selectcountry"
                  placeholder={this.state.selectedval}
                  component={this.renderDropdown}
                  data={Config.countries}
                />
              </View>
              <Text
                style={{
                  textAlign: "center",
                  color: "rgba(44, 57, 63,0.6)",
                  marginTop: 20,
                  fontFamily: "SourceSansPro-Semibold"
                }}
              >
                To:
              </Text>
            </View>
          </View>
          <View>
            <View style={styles.custombuttonView}>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[0].value + 'interestButton'}`}
                  onPress={() =>
                    this._countryIntrest(Config.intrestcountry[0].code)
                  }
                >
                  <Image
                    style={styles.countryIcon}
                    source={this.state.countrybut1
                      ? Images.usa_selected
                      : Images.usa_unavailable}
                  />
                </TouchableOpacity>
              </View>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[1].value + 'interestButton'}`}
                 
                >
                  <Image
                    style={styles.countryIcon}
                    source={this.state.countrybut2
                      ? Images.canada_selected
                      : Images.canada_unavailable}
                  />
                </TouchableOpacity>
              </View>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[2].value + 'interestButton'}`}
                  onPress={() =>
                    this._countryIntrest(Config.intrestcountry[2].code)
                  }
                >
                  <Image
                  
                </TouchableOpacity>
              </View>
            </View>
            <View style={styles.custombuttonView}>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[3].value + 'interestButton'}`}
                  onPress={() =>
                    this._countryIntrest(Config.intrestcountry[3].code)
                  }
                >
                  <Image
                   
                  />
                </TouchableOpacity>
              </View>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[4].value + 'interestButton'}`}
                  onPress={() =>
                    this._countryIntrest(Config.intrestcountry[4].code)
                  }
                >
                  <Image
                    
                </TouchableOpacity>
              </View>
              <View style={{ width: "33%" }}>
                <TouchableOpacity
                  testID={`${Config.intrestcountry[5].value + 'interestButton'}`}
                  onPress={() =>
                    this._countryIntrest(Config.intrestcountry[5].code)
                  }
                >
                  <Image
                   
                  />
                </TouchableOpacity>
              </View>
            </View>
          </View>
          <View>
            <TouchableOpacity
              testID='countrySearchButton'
              onPress={() => !disableButton && this.showAlert1()}
              style={{
              
              }}
            >
              <Text
               
              >
                Search
              </Text>
            </TouchableOpacity>
          </View>
        </View>
      </ScrollView>
    );
  }
  _countryIntrest(code) {
    // alert(code);
    var index = countryIntrest.indexOf(code);
    var butstr = "countrybut" + code;
    if (index == -1) {
      countryIntrest.push(code);
      countryIntrestNow.push(code);
      this.setState({ [butstr]: true });
      this.intialLoadValues();
    } else {
      countryIntrest.splice(index, 1);
      countryIntrestNow.splice(index, 1);
      this.setState({ [butstr]: false });
      this.intialLoadValues();
    }
  }

  _immigreateIntrest = async(code) => {
    if(code === 4){
      )
      return;
    }
    const { isItemSelected } = this.state;
    if (code === "null") {
     
      
      );
    } else {
      await this.setState(
       
      );
    }
  }
  renderScene = ({ route }) => {
    switch (route.key) {
      case 'first':
       
      case 'second':
       
      default:
        return null;
    }
  };
  render() {
    return (
      <View style={{ flex: 1 }}>
        {this.props.isLoading && (
          <View
            style={{
             
            }}
          >
            <Loader />
          </View>
        )}
        <TabView
          removeClippedSubviews={Platform.OS === "android" ? true : false}
          style={{
            backgroundColor: this.state.index === 0 ? "#DBDDDF" : "white"
          }}
          navigationState={this.state}
          removeClippedSubviews={Platform.OS === "android" ? true : false}
          renderTabBar={props => (
            <TabBar
             
              }}
             
             
              pass
              getLabelText={({ route }) => route.title}
            />
          )}
        />
        <UserinfoPopUp
          visible={this.state.userInfoPopUpVisible}
          onClose={()=>this.setState({ userInfoPopUpVisible: false })}
          userPopUpSubmit={this._userPopUpSubmit.bind(this)}
          />
      </View>
    );
  }
}

mapStateToProps = state => ({
  userPressedGo: state.pathReducer.getImmipathDetails.userPressedGo,
});

mapDispatchToProps = dispatch => ({
  dispatch
});

export default compose(
  connect(
    mapStateToProps,
    mapDispatchToProps
  ),
  reduxForm({
    form: "pathexplorertab"
    // validate
  })
)(copilot({
  tooltipComponent: TooltipComponent,
  stepNumberComponent: StepNumberComponent
})(PathexploredTab));

Pathexplorer.js 包含 Pathexplorer 类,其中有一个名为 ToolTipComponent 的组件,我从中调用一个操作,但该操作未反映在 mapStateToProps 中。我被迫这样做的原因是我正在使用一个名为 react-native-copilot 的库,其中我有一个自定义工具提示组件,我想从中访问状态

标签: reactjsreact-nativereduxreact-redux

解决方案


本次活动的问题:

onPress={store.dispatch(action)}

应该:

onPress={()=>store.dispatch(action)}

顺便说一句,您可以连接您的功能组件,如类组件:

export default connect(
    mapStateToProps,
    mapDispatchToProps
  )
)(TooltipComponent)

推荐阅读