Function Description

Click into the details page from the home page of App, and the rotation component starts to load the initial picture from the Store central state warehouse and display it. Then it triggers the drop-down to request new picture, send the updated picture Action, and then update the picture state to the Store central state warehouse through dispatch(Action). Finally trigger the rotation component to re-render, refresh the picture component.

Results demonstrate

Redex

First, let’s take a look at what Redux does when initiating an action externally until state completes the update and sends a notification to subscribers. This can be summarized as follows:

Generally speaking, the process is not complicated:

We use actionCreator to launch the Action. (Although actionCreator is only a specification, you can either dispatch(Action) directly or use bindActionCreators. It depends on you and your team’s style.) Actions are first processed by a series of middlewareXX middleware (note the graphical structure of these middleware, which is similar to KOA’s Onion model, in that they are themselves layered, ) pure function combination receives both the incoming action and the currentState tree (currentState), and sends it to the corresponding reducer to calculate different substates respectively. After calculation, a new state tree is generated. NewState, then assign it to currentState and notify all listeners that the update is complete

Note that the middleware mentioned above is not required, it is only optional, and without it the process will be much simpler: the action goes directly to combination. So with the general flow, let’s look at the concrete implementation of each module

Refer to link technology fat redux’s step-by-step explanation

File directory

reducers/index.js


import {combineReducers} from 'redux'

import appReducer from './AppReducer'
import {resetAppInfo} from '.. /actions/AppActions'
import LoginReducer from './accountReducers/LoginReducer'
import {reducer as formReducer} from 'redux-form'
import {CDManagerReducer} from '.. /component/cdManager'
import {
    RefreshReducer,
} from '.. /component/refreshAble'

import FindHouseReducer from "./homeReducer/FindHouseReducer";

import DetailsReducer from './detailsReducers/DetailsReducer'


function createNamedWrapperReducer(reducerFunction, reducerName) {
    return (state, action) = > {
        const {name} = action;
        const isInitializationCall = state === undefined;
        if(name ! == reducerName && ! isInitializationCall)return state;

        returnreducerFunction(state, action); }}const noResetReducersConfig = {
    loginReducer: LoginReducer,
    app: appReducer,
}

const canResetReducersConfig = {
    form: formReducer,
    cdManager: CDManagerReducer,
    refreshAble: RefreshReducer,

    houseReducer:FindHouseReducer,
    DetailsReducer:DetailsReducer,
    // testList: createNamedWrapperReducer(PullDownRefreshListReducer, 'testList'),
    
}

const reducers = combineReducers(canResetReducersConfig);

constotherReducers = combineReducers({... noResetReducersConfig, ... canResetReducersConfig})export default (state: any, action: any) => {
    if (action.type === resetAppInfo.toString()) {
        let newState = reducers(undefined, action) newState = {... state, ... newState}return otherReducers(newState, action)
    }
    return otherReducers(state, action)
}

Copy the code

/AppAction.js


import { createAction } from 'redux-actions'

export const setAppInfo = createAction('SetAppInfo')
export const resetAppInfo = createAction('ResetAppInfo')

Copy the code

/AppReducer.js



import { handleAction } from 'redux-actions'
import { setAppInfo } from '.. /actions/AppActions'

const initState = {    
    appVersionCode: null.appVersionName: null,}export default function (state = initState, action) {
    return reducer(state, action)
}

const reducer = handleAction(
    setAppInfo,
    (state, action) = >({... state, ... action.payload} ), {}, );Copy the code

/configureStore.js

import {createStore,applyMiddleware} from 'redux';

import reducer from './reducers';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist'
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2'
// import {createBlacklistFilter } from 'redux-persist-transform-filter';
import AsyncStorage from '@react-native-community/async-storage'



// const loginReducerFilter = createBlacklistFilter(
// 'loginReducer',
// ['requesting', 'autologin']
// );
const persistConfig = {
    key: 'root'.storage:AsyncStorage,
    stateReconciler: autoMergeLevel2,
    whitelist: [
        'loginReducer'.'app',].// transforms: [
    // loginReducerFilter,
    / /,
}
const persistedReducer = persistReducer(persistConfig, reducer)


let store = createStore(persistedReducer, applyMiddleware(thunk))

export const persistor = persistStore(store)
export default store
Copy the code

/App.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import 'react-native-gesture-handler';// This sentence can not be deleted, delete may cause the real machine to blink back
import React, {Component} from 'react'

import AppContainer from './AppContainer'

import store ,{persistor}from './configureStore'
import { PersistGate } from 'redux-persist/integration/react'

import { Provider } from 'react-redux';



class Root extends Component {
    render(){
        return(
            
            <Provider store = {store}>
                  <PersistGate loading={null} persistor={persistor}> 
                    <AppContainer />
                   </PersistGate>
            </Provider>)}}export default Root 
Copy the code

⚠️ : React-Redux provides a Provider component and connect method for easy development. As an upper-layer component, the Provider component needs to inject store into the component as a parameter, and then the store object can be accessed in all sub-components. The connect method takes two arguments: MapStateToProps, actionCreators, and returns the processed component, in which mapStateToProps can inject the corresponding state as a prop into the corresponding subcomponent, ActionCreator can inject the corresponding actionCreator as a prop into the corresponding child component.

/index.js

/ * * *@format* /

import {AppRegistry,LogBox} from 'react-native';
import App from './js/App';
import {name as appName} from './app.json';


// Turn off all yellow warnings
LogBox.ignoreAllLogs(true);


if(! __DEV__) {global.console = {
        log: () = >{},error: () = >{},info: () = >{},warn: () = >{},}}// Register global constants
require('./js/common/GlobalValue')



AppRegistry.registerComponent(appName, () = > App);
Copy the code

Using the example

reducers/detailsReducers/DetailsAction.js

/ * * *@flow* /

import {GetHouseDetail,GetHouseTypelist,GetHouseTypeDetail, GetHousePhotos,GetHouseDynamics} from './DetailsActionTypes'

import * as api from '.. /.. /networking/Api'

//redux-thunk
// export function login(values) {
// return (dispatch) => {
// const {userName, password} = values
// dispatch(loginRequest(userName, password))
// asyncLoginRequst(values)
// .then(({state, data}) => {
// if(state === 'gotoHome') {
// dispatch(loginSuccess(data))
// } else {
// dispatch(loginFailed())
/ /}
/ /})
// .catch((error) => {
// if(error instanceof SubmissionError) {
// EDMoney.Toast.show(error.errors._error)
// dispatch(loginFailed())
// } else {
// edmoney.toast.show (' network error ')
// dispatch(networdError())
/ /}
/ /})
/ /}
// }

// Get the photo album
export function getHousePhotosAction(response,refreshing) {
    console.log('refreshingStore',refreshing);
    return {
        type: GetHousePhotos,
        banner:response.banner,
        refreshing:refreshing// Control the loading of small controls}}export async function asyncGetHousePhotos (values) {
    console.log('asyncGetHousePhotos---values',values)
    // Get real estate album (rotation)
    // const values = "1790";
    const response = await api.GetHousePhotos(values)
    // console.log(' Get real estate album ',response);
    let photosData = response.items[0]
    console.log('response',response,'photosData:',photosData);
    if(photosData){
        return Promise.resolve({type: GetHousePhotos, banner: photosData,})
    }
}

export function toGetHousePhotos(values) {
    // When reduex-thunk middleware is used, Function will be return out bindActionCreators (DetailsActions, dispatch) binding and dispatch (DetailsActions. ToGetHousePhotos),
    // Reduex-thunk will determine the contents of the dispatch. When a function is dispatched, it will execute the function directly, and finally become a dispatch object. When dispatch(object) is sent, the reducer is directly reached and the state changes
    return (dispatch,getState) = > {
       var refreshingStore = EDMoney.refreshingStore// Get the status value of the global registry
       console.log('refreshingStore~~~~~',EDMoney.refreshingStore);
    // console.log('getState().DetailsReducer.banner',getState().DetailsReducer); 
        dispatch(getHousePhotosAction(getState().DetailsReducer,refreshingStore.doing))
        asyncGetHousePhotos(values)
            .then((response) = > {
                console.log('response',response);
                console.log('refreshingStore ~! @ # selections ~ ',EDMoney.refreshingStore);
                console.log('getHousePhotosAction(response)1',getHousePhotosAction(response,refreshingStore.done));
                console.log('getHousePhotosAction(response)2', {type:response.type,banner:response.banner,refreshing:refreshingStore.done});
                // dispatch({banner:response.banner,})//
                dispatch(getHousePhotosAction(response,refreshingStore.done))
                console.log('getState',getState());// Get the latest status after update
            })
            .catch((error) = > {
                EDMoney.Toast.show(error)
            })
    }
}



/ / test bindActionCreators
export function test(testData) {
    // The main function of redux-thunk is to dispatch a function instead of just an ordinary Object. As we'll see later, this change gives us tremendous flexibility.
    return (dispatch) = > {
        console.log('Get the dispatch returned by redux-thunk',dispatch);
        dispatch({
            type: 'ceshi',
            testData
        })
        
    }
}

/ / test bindActionCreators
export function test2(testData) {
    return {
        type: 'ceshi2',
        testData,
    }
}




Copy the code

reducers/detailsReducers/DetailsReducer.js

/ * * *@flow* /

import {GetHouseDetail,GetHouseTypelist,GetHouseTypeDetail, GetHousePhotos,GetHouseDynamics} from './DetailsActionTypes'

const initState = {
    banner: {housePhotoTypes:[
            {typeName:'aerial'},
            {typeName:'VR'},
            {typeName:'Renderings'},
            {typeName:'House plan'},
            {typeName:'supporting'},
            {typeName:'real'},
            {typeName:'other'},].housePhotos:[
            {type: "C".picUrl: "https://aikf.hopechina.com/image/bigimg.jpg"},
            {type: "C".picUrl: "https://aikf.hopechina.com/image/bigimg.jpg"},
            {type: "C".picUrl: "https://aikf.hopechina.com/image/bigimg.jpg"},
            {type: "C".picUrl: "https://aikf.hopechina.com/image/bigimg.jpg"},
            {type: "C".picUrl: "https://aikf.hopechina.com/image/bigimg.jpg"},].// Real estate photo album
    },
    testData: {a:'1',},testData2: {a:'2',},refreshing:false
      
}

export default function(state = initState, action) {
    console.log('state!!!!!!! ',state,'action!!!!! ',action);
    if(action.type === GetHousePhotos) {
        // Get the photo album
        return{... state,banner: action.banner,refreshing: action.refreshing}
    }else if(action.type === 'ceshi') {/ / test bindActionCreators
        return{... state,testData: action.testData}
    }else if(action.type === 'ceshi2') {/ / test bindActionCreators
        return{... state,testData2: action.testData}
    }
    return state
}
Copy the code

reducers/detailsReducers/DetailScreen.js

// Created by Li
import React,{Component} from 'react';

import { View,
  Text ,
  Button,
  StyleSheet,
  ScrollView,
  ImageBackground,
  Image,
  TouchableOpacity,
  Dimensions,
  RefreshControl,
} from 'react-native';
import {connect} from 'react-redux'
import { bindActionCreators } from 'redux'
import * as DetailsActions from '.. /.. /reducers/detailsReducers/DetailsAction'
import store from '. /.. /.. /configureStore'

import * as api from '.. /.. /networking/Api'
import ConcactInformation from '.. /.. /component/detail/ConcactInformation'// Intermediary contact information floating window
import StatisticsAudience from '.. /.. /component/detail/StatisticsAudience'// Number of onlookers
import HouseType from '.. /.. /component/detail/HouseType'/ / family
import ItemTitle from '.. /.. /component/detail/ItemTitle'// Customize the title
import AgentHouse from '.. /.. /component/detail/AgentHouse'// Broker real estate
import MapComponent from '.. /.. /component/detail/MapComponent'/ / map
import ShowPictures from '.. /.. /component/detail/ShowPictures'/ / by
import TimeAxis from '.. /.. /component/detail/TimeAxis'// Real estate dynamic timeline

const image = { uri: "https://reactjs.org/logo-og.png" };
// const image = require('./.. /.. /resources/cHomeTab/photo/banner.png')
const opening_time = require('. /.. /.. /resources/cHomeTab/icon/opening_time.png');
const decoration = require('. /.. /.. /resources/cHomeTab/icon/decoration.png');
const developers = require('. /.. /.. /resources/cHomeTab/icon/developers.png');
const property_address = require('. /.. /.. /resources/cHomeTab/icon/property_address.png');
const sharePhoto = require('. /.. /.. /resources/cHomeTab/icon/sharePhoto.png');
// import Banner from './home/component/Swiper_'

const window = Dimensions.get("window");
const screen = Dimensions.get("screen");

class DetailScreen extends Component{
  constructor(props, context) {
    // The Provider is passed to the sub-component through the context, and the sub-component obtains data through connect. The implementation process is as follows.
    // this.store = props.store || context.store 
    super(... arguments)this.state = {
      refreshing:false}}componentDidMount(){
    console.log('this.props.banner------'.this.props.banner);
    const houseId = "1790";
    this.props.toGetHousePhotos(houseId)
    setTimeout(() = >{console.log('this.props.banner+++++'.this.props.banner)},500)
  }

   _onRefresh = () = > {
    console.log('this.props.banner'.this.props.banner);
    console.log('this.props.refreshing'.this.props.refreshing);
    const houseId = "1790";

    Refreshing (refreshing can be controlled by the central state in Store or by setState)
    // this.setState({refreshing:true})
    // DetailsActions.asyncGetHousePhotos(houseId)
    // .then((response) => {
    // this.setState({refreshing:false})
    // if (response.type === 'GetHousePhotos') {
    // console.log('response.banner',response.banner);
    // this.props.added({type:response.type,banner:response.banner})
    // console.log('this.props.banner',this.props.banner);
    // this.props.test({a:'01'});
    // this.props.test2({a:'02'});
    // console.log('this.props.testData',this.props.testData);
    // console.log('this.props.testData2',this.props.testData2);
    / /}
    / /})
    // .catch((error) => {
    // return error
    / /})
    
    Redux-thunk (refreshing needs to be controlled by the central state in Store)
    this.props.toGetHousePhotos(houseId)
    setTimeout(() = >{console.log('After update this.props. Banner'.this.props.banner);console.log('After update this.props. Refreshing'.this.props.refreshing); },500)}render() {
    return (
        <View style={{flex:1}}>
          
          <ScrollView 
          style={styles.Scroll}
          refreshControl={
            <RefreshControl refreshing={this.props.refreshing} onRefresh={()= >{this._onRefresh()}} />
          }>
            <View style={{backgroundColor:'#f5f5f5',}}>{/* picture display */}<ShowPictures navigation={this.props.navigation} />
                <View style={styles.houseMsg}>
                  <View style={styles.houseName}>
                    <Text style={styles.houseTitle}>Poly Fish Port</Text>
                    <Text style={styles.status}>upcoming</Text>
                  </View>
                  <Text style={styles.description}>Above the central axis of the Pearl River, it is located in the core area of the second CBD in Guangzhou</Text>
                  <View style={styles.label}>
                    <Text style={styles.SpecificLabel}>Car with see</Text>
                    <Text style={styles.SpecificLabel}>The elevator room</Text>
                    <Text style={styles.SpecificLabel}>A double</Text>
                    <Text style={styles.SpecificLabel}>International Community</Text>
                  </View>
                  <Text style={styles.price}>36500 yuan/m squared</Text>
                </View>
                
                <View>
                  <View>
                    <View style={styles.projectInformation}>
                      <View style={styles.informationList}>
                        <Image source={opening_time} style={styles.icon}/>
                        <View style={styles.listContent}>
                          <Text style={styles.listText}>Opening time:</Text>
                          <Text style={styles.listText}>In October 2018</Text>
                        </View>
                      </View>
                      <View style={styles.informationList}>
                        <Image source={decoration} style={styles.icon}/>
                        <View style={styles.listContent}>
                          <Text style={styles.listText}>Decoration:</Text>
                          <Text style={styles.listText}>Take decorating</Text>
                        </View>
                      </View>
                      <View style={styles.informationList}>
                        <Image source={developers} style={styles.icon}/>
                        <View style={styles.listContent}>
                          <Text style={styles.listText}>Developers:</Text>
                          <Text style={styles.listText}>Development of poly</Text>
                        </View>
                        </View>
                      <View style={styles.informationList}>
                        <Image source={property_address} style={styles.icon}/>
                        <View style={styles.listContent}>
                          <Text style={styles.listText}>Real Estate Address:</Text>
                          <Text style={styles.listText}>856 Whampoa Avenue BLDG, Whampoa</Text>
                        </View>
                      </View> 
                    </View>
                  </View>
                  <View style={{backgroundColor:'#fff'}} >
                    <TouchableOpacity
                      onPress={()= >{this. Props. Navigation. Navigate (' ProjectInformation ')}} activeOpacity = 0.5} {/ / touch completely opaque, The smaller the value, the more transparent style={styles.moreContainer} ><Text style={styles.more}>More project Information</Text>
                    </TouchableOpacity>
                  </View>
                </View>{/* Number of onlookers */}<StatisticsAudience />
                <View style={styles.projectItem}>
                      <ItemTitle title='Project Introduction'/>
                      <Text style={styles.content}>Poly Yuzhu Port is located in the east of Whampoa Avenue. It is located on the east axis of guangzhou's pearl River development from west to east. It will become a key development area in the next decade along with pearl River New Town. In 2017, the government inscription points out that it will focus on the development of the second business district. Poly Yuzhu is located in the core area of the second CBD, occupying the benefits of urban planning.</Text>
                </View>
                <View style={styles.projectItem}>
                      <ItemTitle title='Thermal pusher' seeMore='See more' navigation={this.props.navigation} />{/* Hot push */}<HouseType />
                </View>
                
                <View style={styles.projectItem}>
                    <ItemTitle title='Developments' count={24}  seeMore='See more' navigation={this.props.navigation} />
                    <TimeAxis />
                </View>
                <View style={styles.projectItem}>
                    <ItemTitle title='Location perimeter' seeMore='See more' navigation={this.props.navigation} />
                    <MapComponent />
                </View>
                <View style={styles.projectItem}>
                    <ItemTitle title='Broker other Real Estate'/>
                    <AgentHouse />
                </View>
            </View>
        </ScrollView>{/* Intermediary contact information */}<ConcactInformation />
      </View>); }}export default connect(
  (state,ownProps) = > {
    console.log('detailState',state)
    console.log('ownProps',ownProps)
      return { 
          userId: state.loginReducer.userId,
          banner:state.DetailsReducer.banner,
          refreshing:state.DetailsReducer.refreshing,
          testData:state.DetailsReducer.testData,
          testData2:state.DetailsReducer.testData2,

      }
  },
  //
  // (dispatch) => { 
  // console.log('dispatch!!!!!!! ',dispatch)
  // // Console. log('123456',bindActionCreators(DetailsActions,dispatch))// The bindActionCreators(DetailsActions,dispatch) format returns a key of Name of each Action function in DetailsActions
  // // console.log('78910',{... bindActionCreators(DetailsActions,dispatch)})//{... The bindActionCreators(DetailsActions,dispatch)} variant returns a key as the name of each Action function in the DetailsActions
  // return {
  / /... bindActionCreators(DetailsActions,dispatch)
  // ** or:
  // * add: (data) =>{console.log('data',data); dispatch(data); },
  // * added: (data) =>{console.log('data',data); dispatch(data); },
  / / *
  / / * * /
      
  / /}
  // }

  //
  // (dispatch) => (
  //   {...bindActionCreators(DetailsActions,dispatch)}
  // )

  //
  // (dispatch) => ({
  // bindActionCreators({
  // actionCreator1: actionCreator1,
  // actionCreator2: actionCreator2
  // })},dispatch)
    / / call way: / / this props. ActionCreator1 ()

  //
  (dispatch) = > ({
    ...bindActionCreators(DetailsActions,dispatch),
    // Asynchronous operations are conveniently written as follows
    add: (data) = >{console.log('data',data); dispatch(data); },added: (data) = >{console.log('data',data); dispatch(data); }, }) )(DetailScreen)var styles = StyleSheet.create({
  Scroll: {backgroundColor:'#ffffff'
  },
  flexLayout: {flexDirection:'row',},shareIcon: {position:"absolute".top:10.right:30.width:40.height:40,},sharePhotoIcon: {width:30.height:30.borderRadius:14
  },
  projectInformation: {paddingLeft:15.backgroundColor:'#ffffff'.paddingBottom:10
  },
  informationList: {
    flexDirection:'row'.alignItems:'center',},listContent: {flexDirection:'row'
  },
  icon: {marginRight:5
  },
  listText: {color:'# 222222'.fontSize:16.lineHeight:30
  },
  morecontainer: {backgroundColor:'#f5f5f5'.alignItems:'center'.borderRadius:3.overflow:'hidden'.marginLeft:15.marginRight:15
  },
  more: {lineHeight:45.fontSize:17.color:'#495b84',},houseMsg: {paddingLeft:15.paddingRight:15.backgroundColor:'#ffffff'.paddingTop:20
  },
  houseName: {flexDirection:'row'.alignItems:'center',},houseTitle: {color:'# 222222'.fontSize:20.lineHeight:25.marginRight:10
  },
  status: {height:25.lineHeight:25.backgroundColor:'#fae5e4'.color:'#ec736f'.borderRadius:5.overflow:'hidden'.paddingLeft:5.paddingRight:5.fontSize:13
  },
  description: {height:25.lineHeight:25.color:'# 545454',},label: {flexDirection:'row',},SpecificLabel: {height:20.lineHeight:20.fontSize:13.color:'#495b84'.backgroundColor:'#f5f5f5'.paddingLeft:5.paddingRight:5.borderRadius:3.overflow:'hidden'.marginRight:10
  },
  price: {fontSize:25.color:'#eb5e36'.lineHeight:50
  },
  projectItem: {paddingLeft:15.paddingRight:15.backgroundColor:'#ffffff'.marginTop:10.paddingTop:20.paddingBottom:20
  },
  content: {color:'# 545454'.fontSize:16.lineHeight:22}})Copy the code