آموزش Redux  در React Native

28 مرداد 1397
react-native-redux

Redux یک کتابخانه مستقل برای مدیریت State ها است که می تواند با هر کتابخانه و فریم ورکی استفاده شود. در صورتی که با React آشنایی داشته باشید، Redux را همراه با React استفاده کرده اید.

کاربرد اصلی Redux این است که می توانیم از یک وضعیت (state) به عنوان state سراسری استفاده کنیم و به آسانی از هر کامپوننت Reactیی به این state سراسری دسترسی داشته باشیم، چه این کامپوننت جزو سلسله مراتب آن باشد یا رابطه والد-فرزند با آن داشته باشد.

سرفصل های این آموزش

1- نصب React Native

2- افزودن Textbox و دکمه به App.js

3-تعریف state و input handler

4- ایجاد فولدرهای مربوطه در روت پروژه

5- ایجاد یک تابع Reducer

6- ایجاد یک Redux Store

7-پاس دادن Store به برنامه React Native

8-اتصال برنامه React Native به Redux Store

1- نصب React Native

کدهای زیر را در ترمینال وارد کنید.

npm install -g react-native-cli

یک برنامه جدید ایجاد کنید

react-native init rncreate
cd rncreate

بعد از نصب، برنامه را در هر دو شبیه ساز ios و اندروید باز کنید.

react-native run-ios

درصورتی که XCode را به درستی پیکربندی کرده باشید، باید شبیه ساز ios به درستی باز شود.

با دستور زیر برنامه را در شبیه ساز اندروید اجرا کنید.

react-native run-android

کتابخانه های react-redux و redux را با دستور زیر نصب کنید.

yarn add redux react-redux

# or

npm install redux react-redux --save

2- افزودن Textbox و Button به فایل App.js

حال یک دکمه و Textbox برای افزودن اسم مکان ها و همچنین لایوت flexbox را اضافه می کنیم. کدهای زیر را در فایل App.js قرار دهید.

// App.js

import React, {Component} from 'react';
import { StyleSheet, View, TextInput, Button } from 'react-native';

export default class App extends Component {

placeSubmitHandler = () => {
    console.log("Submitted");	
}

render() {
   return (
    <View style={ styles.container }>
       <View style = { styles.inputContainer }>
        <TextInput
           placeholder = "Seach Places"
           style = { styles.placeInput }
        ></TextInput>
        <Button title = 'Add' 
            style = { styles.placeButton }
            onPress = { this.placeSubmitHandler }
        />
        </View>
    </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    paddingTop: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%'
  },
  placeInput: {
    width: '70%'
  },
  placeButton: {
    width: '30%'
  },
  listContainer: {
    width: '100%'
  }
});

3- تعریف State و input handler

حال یک state برای مدیریت برنامه ایجاد میکنیم.

// App.js

state = {
   placeName: '',
   places: []
}

placeSubmitHandler = () => {
    console.log("Submitted");	
}

4- ایجاد فولدرهای مربوطه در روت پروژه

فولدرهای زیر را در روت پروژه ایجاد کنید.

  • actions
  • reducer
  • components

داخل فولدر actions یک فایل به نام type.js اضافه کرده و کدهای زیر را در آن قرار دهید.

export const ADD_PLACE = 'ADD_PLACE'

نوع اکشن از نوع reducer است. بسته به نوع اکشن، reducer مربوطه اجرا می شود و می توانیم state را به این طریق تغییر دهیم.

بنابراین یک کپی از state موجود ایجاد کرده و state جدید را بر می گردانیم.

حال یک فایل دیگر به نام place.js در همان دایرکتوری ایجاد کنید.

// place.js

import { ADD_PLACE } from './types';

export const addPlace = placeName => {
  return {
    type: ADD_PLACE,
    payload: placeName
  }
}

تابع addPlace() یک اکشن را بر می گرداند. حال با توجه به نوع اکشن برگشتی میتوانیم تابع reducer مربوطه را اجرا کنیم.

اما ما باید به یک روشی این اکشن را به کامپوننت App.js متصل کنیم، در غیر اینصورت نمی توانیم داده ها را داخل Redux Store اضافه کنیم.

همچنین ابتدا باید یک Store ایجاد کنیم. اما قبل از آن باید یک تابع reducer بوجود بیاوریم. پس ابتدا یک reducer و بعد از آن Store را ایجاد و سپس برنامه React Native را به Redux store متصل می کنیم.

5- ایجاد یک تابع Reducer

داخل فولدر reducer یک فایل به نام placeReducer.js ایجاد و کدهای زیر را در آن قرار می دهیم.

// placeReducer.js

import { ADD_PLACE } from '../actions/types';

const initialState = {
  placeName: '',
  places: []
};

const placeReducer = (state = initialState, action) => {
  switch(action.type) {
    case ADD_PLACE:
      return {
        ...state,
        places: state.places.concat({
          key: Math.random(),
          value: action.payload
        })
      };
    default:
      return state;
  }
}

export default placeReducer;

در کدهای بالا یک تابع به نام placeReducer ایجاد کردیم که دو آرگومان می گیرد:

  1. state
  2. action

ابتدا initialState برنامه مان را گرفته و آن را به عنوان آرگومان به PlaceReducer پاس می دهیم و با توجه به نوع اکشن، عملیات مربوطه را اجرا می کنیم. آرگومان دوم برابر action است که شامل type و Payload است. Payload در واقع همان placeName است و مقدار textbox را گرفته و آن را در آرایه placeName وارد می کنیم.

دقت کنید در اینجا ما State جدید را بر میگردانیم نه State جاری. بنابراین ما این state جدید را تغییر می دهیم نه state جاری.

6- ایجاد یک Redux Store

داخل روت پروژه یک فایل به نام Store.js ایجاد و کدهای زیر را در آن قرار می دهیم.

// store.js

import { createStore, combineReducers } from 'redux';
import placeReducer from './reducers/placeReducer';

const rootReducer = combineReducers({
  places: placeReducer
});

const configureStore = () => {
  return createStore(rootReducer);
}

export default configureStore;

در کدهای بالا ما یک Redux Store ایجاد کرده و reducer را به Store  پاس می دهیم. تابع combineReducer همه reducerهای مختلف را به یک reducer ترکیب می کند و State سراسری را شکل می دهد.

پس این state سراسری برای کل برنامه است.

7- پاس دادن Store به برنامه React Native

داخل روت پروژه فایل index.js را پیدا کرده و کدهای زیر را در آن فایل اضافه کنید.

// index.js

import { AppRegistry } from 'react-native';
import React from 'react';
import App from './App';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';

import configureStore from './store';

const store = configureStore()

const RNRedux = () => (
  <Provider store = { store }>
    <App />
  </Provider>
)

AppRegistry.registerComponent(appName, () => RNRedux);

این تقریباً مثل برنامه React است و ما provider را به عنوان عنصر ریشه پاس داده و سپس با استفاده از تابع connect() مربوط به کتابخانه react-redux می توانیم هر نوع کامپوننت React را به Redux Store پاس می دهیم.

8- اتصال برنامه React Native به Redux Store

در انتها کامپوننت App.js را به Redux store پاس می دهیم.برای اینکار به تابع connect() از کتابخانه react-redux نیاز داریم

// App.js

import React, { Component } from 'react';
import { StyleSheet, View, TextInput, Button, FlatList } from 'react-native';
import ListItem from './components/ListItem';
import { connect } from 'react-redux';
import { addPlace } from './actions/place';

class App extends Component {

  state = {
    placeName: '',
    places: []
  }

  placeSubmitHandler = () => {
    if(this.state.placeName.trim() === '') {
      return;
    }
    this.props.add(this.state.placeName);
}

placeNameChangeHandler = (value) => {
  this.setState({
    placeName: value
  });    
}

placesOutput = () => {
   return (
    <FlatList style = { styles.listContainer }
      data = { this.props.places }
      keyExtractor={(item, index) => index.toString()}
      renderItem = { info => (
        <ListItem 
          placeName={ info.item.value }
        />
      )}
    />
  )
}

render() {
  return (
    <View style={ styles.container }>
      <View style = { styles.inputContainer }>
        <TextInput
          placeholder = "Seach Places"
          style = { styles.placeInput }
          value = { this.state.placeName }
          onChangeText = { this.placeNameChangeHandler }
        ></TextInput>
        <Button title = 'Add' 
          style = { styles.placeButton }
          onPress = { this.placeSubmitHandler }
        />
        </View>
        <View style = { styles.listContainer }>
          { this.placesOutput() }
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    paddingTop: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%'
  },
  placeInput: {
    width: '70%'
  },
  placeButton: {
    width: '30%'
  },
  listContainer: {
    width: '100%'
  }
});

const mapStateToProps = state => {
  return {
    places: state.places.places
  }
}

const mapDispatchToProps = dispatch => {
  return {
    add: (name) => {
      dispatch(addPlace(name))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

در کد بالا، هنگامی که روی دکمه add کلیک کنیم، برنامه مقدار textbox را گرفته و آن را به اکشن ارسال میکند و سپس اکشن یک آبجکت همراه با نوع اکشن و payload را برگردانده و بسته به نوع اکشن، Reducer مربوطه اجرا شده و مقدار آن را به Store اضافه می کند.

حال اگر مقدار store تغییر کرد، باید رابط کاربری برنامه (UI) را بسته به نوع مقادیر جدید، بروزرسانی کنیم که برای اینکار از تابع mapStateToProps را ایجاد کردیم.

سپس هنگامی که places array که همان stateمان است، یک مقدار جدید بگیرد، تابع render() اجرا شده و رابط کاربری برنامه را بروزرسانی می کند.

تابع mapDispatchToProps کمک می کند تا برنامه را به اکشن مورد نیاز متصل کنیم. سپس این اکشن به وسیله reducer اجرا می شود و state برنامه را تغییر می دهد.

همچنین داخل فولدر components یک فایل به نام ListItem.js ایجاد و کدهای زیر را در آن قرار می دهیم.

// ListItem.js

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';

const ListItem = (props) => {
    return (
      <TouchableOpacity>
        <View style = { styles.listItem }>
          <Text>{ props.placeName }</Text>
        </View>
      </TouchableOpacity>
    );
}

const styles = StyleSheet.create({
  listItem: {
    width: '100%',
    padding: 10,
    marginBottom: 10,
    backgroundColor: '#eee'
  }
});

export default ListItem;

این کامپوننت پروپرتی ها را از کامپوننت والد گرفته و داده ها را به یک شکل مناسب نمایش می دهد. فایل را ذخیره کنید و نتیجه را در شبیه ساز بینید.

نمایش آیتم ها در لیست React و Redux

نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

حمیدرضاغفوری
03 دی 1399
سلام دو typo وجود داره اول اینکه در قسمتی که میگید پوشه ایجاد کنید باید types.js باشه که به اشتباه type.js نوشته شده. دوم اینکه در index.js روت دهی store.js غلط بوده که باید به Store.js تبدیل بشه. در نهایت ممنون خیلی کاربردی بود...

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.