استفاده از ریداکس در اپلیکیشن ری اکت — راهنمای کاربردی

۳۹ بازدید
آخرین به‌روزرسانی: ۱۷ اسفند ۱۳۹۸
زمان مطالعه: ۴ دقیقه
استفاده از ریداکس در اپلیکیشن ری اکت

فرض کنید تصمیم گرفته‌اید از ریداکس (Redux) به عنوان کتابخانه مدیریت حالت استفاده کنید. باور کنید یا نه این بخش یعنی تصمیم‌گیری در مورد کتابخانه‌ای که باید استفاده شود، سخت‌ترین بخش کار است. بقیه مراحل آسان و سریع هستند. در این راهنما با ما همراه باشید تا در عرض چند دقیقه با استفاده از ریداکس در اپلیکیشن ری اکت یک نرم‌افزار ToDo بسازیم.

معرفی ریداکس

ریداکس سه کامپوننت عمده به نام‌های store ،reducer و actions دارد. بر اساس مستندات ریداکس:

  • کامپوننت Store «حالت» (State) (درخت حالت) اپلیکیشن را نگهداری می‌کند. تنها روش برای تغییر حالت درون آن، این است که یک اکشن را به آن ارسال (Dispatch) کنید.
  • کامپوننت Actions در واقع payload اطلاعاتی است که داده‌ها را از اپلیکیشن به استور ارسال می‌کنند. اکشن‌ها تنها منبع اطلاعات برای استور محسوب می‌شوند.

ما دو اکشن تعریف می‌کنیم که createItem و deleteItem هستند.

  • «ردیوسرها» (Reducers) نیز شیوه مدیریت حالت از سوی اپلیکیشن را در پاسخ به اکشن‌های ارسال شده به استور تعیین می‌کنند. به خاطر داشته باشید که اکشن‌ها صرفاً آن چه رخ داده را توصیف می‌کنند، اما چگونگی تغییر یافتن حالت اپلیکیشن را توضیح نمی‌دهند.

یک ردیوسر در فرایندی شبیه به ایجاد یک جدول در پایگاه داده SQL ایجاد می‌شود. شیوه ساختن آن، بسته به نوع اپلیکیشن متفاوت خواهد بود.

راهنمای عملی

در این بخش مراحل راهنمای عملی ساخت اپلیکیشن ToDo و استفاده از ریداکس را توضیح می‌دهیم.

گام 1

با استفاده از اسکریپت create-react-app یک اپلیکیشن جدید ری‌اکت بسازید:

create-react-app todo-app

گام 2

وابستگی‌ها را با دستور زیر نصب کنید:

npm install lodash @material-ui/core @material-ui/icons react-redux redux redux-logger

گام 3

اگر می‌خواهید روشی مؤثر برای ذخیره فایل‌ها بیابید، از ساختار فایل ducks بهره بگیرید. اما در این راهنما به منظور سادگی از ساختار فایل ساده‌ای استفاده می‌کنیم.

پوشه جدیدی به نام modules ایجاد کنید. سپس سه فایل به نام‌های action.js، reducer.js و store.js در این پوشه بسازید. Actions اشیای ساده‌ای با یک خصوصیت الزامی به نام type هستند. اکشن‌ها را از کامپوننت Dispatch می‌کنیم تا داده‌ها به حالت استور ارسال شوند:

فایل Actions.js

// types of action
const Types = {
  CREATE_ITEM: "CREATE_ITEM",
  DELETE_ITEM: "DELETE_ITEM"
};
// actions
const createItem = task => ({
  type: Types.CREATE_ITEM,
  payload: task
});

const deleteItem = id => ({
  type: Types.DELETE_ITEM,
  payload: id
});

export default {
  createItem,
  deleteItem,
  Types
};

روش بهینه این است که یک فایل دیگر برای نوع اکشن‌ها ایجاد کنید. به این منظور فایل reducer.js را ساخته و محتوای زیر را به آن اضافه می‌کنیم:

import ACTIONS from "./action";
import _ from "lodash";

const defaultState = {
  items: []
};

const todoReducer = (state = defaultState, action) => {
  switch (action.type) {
    case ACTIONS.Types.CREATE_ITEM: {
      console.log(action);

      let item = action.payload;
      let newItem = { id: state.items.length + 1, description: item };
      let newState = _.cloneDeep(state);
      newState.items.push(newItem);
      return newState;
    }

    case ACTIONS.Types.DELETE_ITEM: {
      let newState = _.cloneDeep(state);
      let index = _.findIndex(newState.items, { id: action.payload });
      newState.items.splice(index, 1);
      return newState;
    }

    default:
      return state;
  }
};

export default todoReducer;

فایل store.js نیز کدهایی به شکل زیر دارد:

import { createStore, applyMiddleware } from "redux";

// Logger with default options
import logger from "redux-logger";

import reducer from "./reducer";

export default function configureStore(initialState) {
  const store = createStore(reducer, initialState, applyMiddleware(logger));
  return store;
}

گام 4

فایل جدیدی به نام todo.js در پوشه جدیدی به نام pages درون پوشه src بسازید. این فایل شامل کامپوننت لیست ToDo خواهد بود. در این مثال روی ریداکس متمرکز شده‌ایم، بنابراین تلاش نمی‌کنیم که کامپوننت/کانتینر ایجاد کنیم. کل اپلیکیشن ما درون همین تک فایل قرار خواهد داشت. UI را با یک فرم و یک لیست برای نمایش وظیفه‌ها ایجاد می‌کنیم.

فایل todo.js

import React, { Component } from "react";
import {
  withStyles,
  List,
  ListItem,
  ListItemSecondaryAction,
  ListItemText,
  IconButton,
  Grid,
  TextField,
  Button,
  FormControl
} from "@material-ui/core";
import DeleteIcon from "@material-ui/icons/Delete";

const styles = theme => ({
  root: {
    flexGrow: 1,
    maxWidth: 752
  },
  demo: {
    backgroundColor: theme.palette.background.paper
  },
  title: {
    margin: `${theme.spacing.unit * 4}px 0 ${theme.spacing.unit * 2}px`
  }
});

class ToDO extends Component {
  state = {};

  generate = () => {
    return this.props.items.map(item => (
      <ListItem key={item.id}>
        <ListItemText primary={item.description} />
        <ListItemSecondaryAction>
          <IconButton
            aria-label="Delete"
            onClick={this.handleDelete}
            value={item.id}
          >
            <DeleteIcon />
          </IconButton>
        </ListItemSecondaryAction>
      </ListItem>
    ));
  };

  handleSubmit = event => {
    // console.log(this.state.item);
    this.setState({ item: "" });
    if (this.state.item !== "") {
      // add the task to store

    }
    event.preventDefault();
  };
  handleDelete = event => {
    //delete the task from the store
    
  };
  handleChange = event => {
    this.setState({
      [event.target.name]: event.target.value
    });
  };

  render() {
    const { classes } = this.props;

    return (
      <div>
        <div>
          <form noValidate autoComplete="off" onSubmit={this.handleSubmit}>
            <FormControl>
              <TextField
                label="New Task"
                id="margin-dense"
                value={this.state.item}
                className={classes.textField}
                margin="dense"
                name="item"
                onChange={this.handleChange}
              />
            </FormControl>
            <FormControl>
              <Button>Add</Button>
            </FormControl>
          </form>
        </div>
        <div>
          <Grid item container justify="space-evenly" alignItems="center">
            <div className={classes.demo}>
              <List dense={false}>{this.generate()}</List>
            </div>
          </Grid>
        </div>
      </div>
    );
  }
}

export default withStyles(styles)(ToDO);

گام 5

اینک زمان آن رسیده است که کامپوننت todo را به استور ریداکس وصل کنیم. به این منظور فایل App.js را باز کنید و ارائه‌دهنده ریداکس را اضافه کنید تا همه فرزندان بتوانند به استور دسترسی داشته باشند:

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import ToDO from "./pages/todo";
import { Provider as ReduxProvider } from "react-redux";
import configureStore from "./modules/store";

const reduxStore = configureStore(window.REDUX_INITIAL_DATA);

class App extends Component {
  render() {
    return (
      <ReduxProvider store={reduxStore}>
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <h1 className="App-title">ToDo Redux app</h1>
          </header>
          <ToDO />
        </div>
      </ReduxProvider>
    );
  }
}

export default App;

درون فایل todo.js آن را با استفاده از کامپوننت connect از react-redux به استور وصل کنید. این موارد را به انتهای فایل todo.js اضافه کنید. اکنون آیتم‌های استور در props کامپوننت نیز در دسترس هستند. ضمناً دو تابع (اکشن) دیگر نیز در props موجود خواهند بود.

import ACTIONS from "../modules/action";
import { connect } from "react-redux";

const mapStateToProps = state => ({
  items: state.items
});

const mapDispatchToProps = dispatch => ({
  createItem: item => dispatch(ACTIONS.createItem(item)),
  deleteItem: id => dispatch(ACTIONS.deleteItem(id))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withStyles(styles)(ToDO));

در نهایت createItem و deleteItem را به ترتیب درون handleSubmit و handleDelete فراخوانی می‌کنیم:

handleSubmit = event => {
  // console.log(this.state.item);
  this.setState({ item: "" });
  if (this.state.item !== "") {
    // add the item to the store
    this.props.createItem(this.state.item);
  }
  event.preventDefault();
};

handleDelete = event => {
  // delete the item from the store
  this.props.deleteItem(event.target.value);
};

سخن پایانی

اگر می‌خواهید کل کد اپلیکیشنی که در این راهنما ساختیم را بررسی کنید، می‌توانید به این ریپازیتوری گیت‌هاب (+) مراجعه کنید. تجربیات خود را در مورد روش استفاده از ریداکس در اپلیکیشن ری‌اکت در بخش نظرات این نوشته با ما و دیگر خوانندگان فرادرس در میان بگذارید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
wineofbits

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *