Tìm hiểu redux thông qua ví dụ

Redux là gì ?

Redux là một thư viện Javascript cho phép ta quản lý state của containter một cách hiệu quả. Có thể sử dụng Redux với React hoặc các View library khác.

Cấu tạo của Redux ?

Trong Redux thì container(smart component), action, reducer, store là những thành phần chính

Action

Action là nơi bắt các sự kiện click, delete .... và trả về:

  • Action type
  • New state cho state tương ứng
export const addTodo = (text) => {
  return {
    type: 'ADD_TODO',
    text,
    completed: false
  }
};


export const toggleTodo = (id) => {
  return {
    type: 'TOGGLE_TODO',
    id
  }
};

export const setVisibleFilter = (filter) => {
  return {
    type : 'VISIBLE_FILTER',
    filter
  }
};

Reducer

Có thể coi reducer là một bộ chuyển đổi với Input: State hiện tại, action Output: New state tương ứng

const initialState = {
  todos: [],
  nextId: 0
};

export default (state = initialState, action) => {
  switch (action.type){
    case 'ADD_TODO':
      return {
        todos: [
          ...state.todos,
          {
            id: state.nextId + 1,
            text: action.text,
            completed: action.completed
          }
        ],
        nextId: state.nextId + 1
      };
    case 'TOGGLE_TODO':
      return {
        todos: state.todos.map(todo =>
          todo.id === action.id ?  { id: todo.id, text: todo.text, completed: true } : todo
        ),
        nextId: state.nextId
      }
    default:
      return state;
  }
};

Store

Là nơi lưu lại tất cả các state trong ứng dụng. Mỗi reducer sẽ xử lý và trả về state. Tập hợp các state này sẽ thành cây state mà Store nắm giữ.

import {combineReducers} from 'redux'
import todos_reducer from './todos'
import visible_filter_reducer from './visible_filter'

export const rootreducer = combineReducers({todos: todos_reducer, visible_filter: visible_filter_reducer});

Ở đây mình có 2 reducer todo_reducer và visible_reducer Cây state của mình sẽ là một object có dạng

{todos: data, visible_reducer: data}

Tạo store và truyền cho Provider để tạo ra một mạng lưới kết nối

const store = createStore(rootreducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
<Provider store={store}>
    <App/>
</Provider>, document.getElementById('root')

Container

Là các Component tương tác trực tiếp với Store. Còn lại là các component không tương tác với Store(dump component). Container tương tác với Store thông qua function connect() connect() nhận vào 4 tham số mapStateToProps mapDispatchToProps mergeProps options. Mình chỉ mới sử dụng mapStateToProps và mapDispatchToProps

  • mapStateToProps(state, ownProps) là một function. Sử dụng nó khi muốn Container nhận data khi cây State thay đổi. Cứ mỗi khi cây state thay đổi thì mapStateToProps sẽ được gọi. Nếu ownProps được định nghĩa thì nó là giá trị props được gửi cho container. Như đoạn code phía dưới thì mỗi khi cây state thay đổi thì sẽ trả về cho TodoList một data là kết quả của function filter(state.todos, state.visible_filter).
const filter = (todos, filter) => {
  switch (filter){
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_ACTIVE':
      return {
        todos: todos.todos.filter(todo => !todo.completed),
        nextTodoId: todos.nextTodoId
      };
    case 'SHOW_COMPLETED':
      return {
        todos: todos.todos.filter(todo => todo.completed),
        nextTodoId: todos.nextTodoId
      };
    default:
      throw new Error('Have no filter');
  }
};
class TodoList extends Component{
  render(){
    return(
      <ul>
        {
          this.props.todos.map(todo =>
            <Todo text={todo.text} key={todo.id} id={todo.id}/>
          )
        }
      </ul>
    )
  }
}

const mapStateToProps = (state, ownprops) => {
  return filter(state.todos, state.visible_filter)
};

export default connect(mapStateToProps, null)(TodoList)
  • mapDispatchToProps là object hoặc function. Sử dụng nó khi muốn Container có thể call được action thông qua props của mình. Nếu là object thì mỗi function bên trong sẽ có thể được gọi trong Container thông qua props của nó Như đoạn code ở dưới, mỗi khi click và thẻ <a> thì sẽ thực hiện action setVisibleFilter thông qua props của nó. Hàm connect()(FilterLink) là để FilterLink có thể gọi được action setVisibleFilter thông qua props của nó (this.props.setVisibleFilter)
import {setVisibleFilter} from "../../actions";
class FilterLink extends Component{
  constructor(){
    super();
    this.handleSetvisibleFilter = this.handleSetvisibleFilter.bind(this)
  }
  handleSetvisibleFilter(e){
    e.preventDefault();
    this.props.setVisibleFilter(this.props.filter_text);
  }
  render(){
    return(
      <a href="" onClick={e=> this.handleSetvisibleFilter(e)}>{this.props.filter_text}</a>
    )
  }
}

FilterLink.propTypes = {
  filter_text: PropTypes.string.isRequired
};


export default connect(null, {setVisibleFilter})(FilterLink)

Trên đây là những gì mình tìm hiểu được khi tự tìm hiểu và làm lại một vài ví dụ sử dụng redux. Có gì thiếu sót mong mọi người thông cảm. Source code: https://github.com/thonglhuet/Todo_redux Mọi người cũng có thể tham khảo các ví dụ trên https://github.com/reactjs/redux/tree/master/examples