React + Redux Back

Actually Redux has no relation to React, but most people will use it with React, because in React, you can describe your UI with state, which can be managed by Redux.

Installation

React bindings are not included in Redux by default. You need to install them explicitly:

npm install react-redux --save

Presentational and Container Components

React bindings for Redux embrace the idea of separating presentational and container components.

Presentational Components Container Components
Purpose How things look (markup, styles) How things work (data fetching, state updates)
Aware of Redux No Yes
To read data Read data from props Subscribe to Redux state
To change data Invoke callbacks rom props Dispathc Redux actions
Are written By hand Usually generated by Reat Redux

Technically you could write the container components by hand using store.subscribe(). We don't advise you to do this because React Redux makes many performance optimizations that are hard to do by hand. For this reason, rather than write container components, we will generate them using the connect() function provided by React Redux, as you will see below.

Presentational Components

  • TodoList: a list showing visible todo items.
    • todos: Array is an array of todo items with {id, text, completed} shape.
    • onTodoClick(id: number) is a callback to invoke when a todo is clicked.
  • Todo: a single todo item.
    • test: string is the text to show
    • completed: boolean is whether todo item is completed.
    • onClick() invoke owner's function onTodoClick().
  • Link: a link with a callback
    • onClick() is a callback to invoke when a link is clicked.
  • Footer: where we let the user change currently visibile todos;
  • App: the root component that renders everything else.

Container Components

  • VisibilityTodoList: to filter the todos according to the current visibility filter and renders a TodoList.
  • FilterLink: to get he current visibility filter and render a Link.
    • filter: string is the visibility filter it represents.

Other Components

  • AddTodo: an input field with an "Add" button

Implementing Components

Presentational Components


{/** components/Todo.jsx */}

import React, { PropTypes } from 'react';

const Todo = ({ onClick, completed, text }) => {
    return (
        <li
            onClick={onClick}
            style={
                {
                    textDecoration: completed ? 'line-through' : 'none'
                }
            }
        >
            {text}
        </li>
    );
};

Todo.propTypes = {
    onClick: PropTypes.func.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
};


{/** components/TodoList.jsx */}

import React, { PropTypes } from 'react';
import Todo from './Todo.jsx';

const TodoList = ({ todos, onTodoClick }) => {
    return (
        <ul>
            {
                todos.map((todo) => (
                    <Todo
                        key={todo.id}
                        {..todo}
                        onClick={() => onTodoClick(todo.id)}
                    />
                ));
            }
        </ul>
    );
};

TodoList.propTypes = {
    todos: PropTypes.arrayOf(
        PropTypes.shape({
            id: PropTypes.number.isRequired,
            completed: PropTypes.bool.isRequired,
            text: PropTypes.string.isRequired
        }).isRequired
    ).isRequired;
};

export default TodoList;


{/** components/Link.jsx */}

import React, { PropTypes } from 'react';

const Link = ({ active, children, onClick }) => {
    if (actve) {
        return (
            <span>{children}</span>
        );
    }

    return (
        <a
            href="#"
            onClick={
                (e) => {
                    e.preventDefault();
                    onClick();
                }
            }
        >
            {children}
        </a>
    );
};

Link.propTypes = {
    active: PropTypes.bool.isRequired,
    children: PropTypes.node.isRequired,
    onClick: PropTypes.func.isRequired
};

export default Link;


{/** components/Footer.jsx */}

import React, { PropTypes } from 'react';
import FilterLink from './../containers/FilterLink.jsx';

const Footer = () = {
    return (
        <p>
            Show    
            {' '}
            <FilterLink filter="SHOW_ALL">
                All
            </FilterLink>
            {", "}
            <FilterLink filter="SHOW_ACTIVE">
                Active
            </FilterLink>
            {", "}
            <FilterLink filter="SHOW_COMPLETED">
                Completed
            </FilterLink>
        </p>
    );
};

export default Footer;


{/** components/App.jsx */}

import React from 'react';
import Footer from './Footer';
import AddTodo from '../containers/AddTodo';
import VisibleTodoList from '../containers/VisibleTodoList';

const App = () = {
    return (
        <div>
            <AddTodo />
            <VisibleTodoList />
            <Footer />
        </div>
    );    
};

Container Components

In Redux, you don't have to produce contianer components by hand, and just let connect() function to do this for you, which provides many useful optimizations to prevent unnecessary re-renders.

Before using connect(), you should define a special function called mapStateToProps that tells how to transform state into props.

For example, VisibleTodoList needs to calculate todos to pass to the TodoList, so we define a function that filters the state.todos according to the state.visibilityFilter, and use it in its mapStateToProps:


const getVisibleTodos = (todos, filter) => {
    switch (filter) {
    case 'SHOW_ALL':
        return todos;
    case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed);
    }
};

const mapStateToProps = (state) => {
    return {
        todos: getVisibleTodos(state.todos, state.visibilityFilter)
    };
};

Besides, container components can dispatch actions by defining a function called mapDispatchToProps.

For example, we want the VisibleTodoList to inject a prop called onTodoClick into the TodoList component, and we want onTodoClick to dispatch a TOGGLE_TODO action:


const mapDispatchToProps = (dispatch) => {
    return {
        onTodoClick: (id) => {
            dispatch(toggleTodo(id))
        }
    };
};

With those two functions we have just defined, we can use connect() to create the component VisibleTodoList.


{/** containers/VisibleTodoList.jsx */}
import { connect } from 'react-redux';
import { toggleTodo } from '../actions.js';
import TodoList from '../components/TodoList';

const getVisibleTodos = (todos, filter) => {
    switch (filter) {
    case 'SHOW_ALL':
        return todos;
    case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed);
    }
}

const mapStateToProps = (state) => {
    return {
        todos: getVisibleTodos(state.todos, state.visibilityFilter)
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onTodoClick: (id) => {
            dispatch(toggleTodo(id));
        }
    };
};

const VisibleTodoList = connect(
    mapStateToProps,
    mapDispatchToProps
)(TodoList);

export default VisibleTodoList;


{/** containers/FilterLink.jsx */}
import { connect } from 'react-redux';
import { setVisibilityFilter } from '../actions.js';
import Link from '../components/Link';

const mapStateToProps = (state, ownProps) => {
    return {
        active: ownProps.filter ==== state.filter
    };
};

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onClick: () => {
            dispatch(setVisibilityFilter(ownProps.filter));
        }
    };
};

const FilterLink = connect(
    mapStateToProps,
    mapDispatchToProps
)(Link);

export default FilterLink;


{/** containers/AddTodo.jsx */}
import React from 'react';
import { connect } from 'react-redux';
import { addTodo } from '../actions.js';

let AddTodo = ({ dispatch }) => {
    let input;

    return (
        <form
            onSubmit = (e) => {
                e.preventDefault();

                if (!input.value.trim()) {
                    return;
                }

                dispatch(addTodo(input.value));
                input.value = '';
            }
        >
            <input
                ref = {
                    node => {
                        input = node;
                    }
                }
            ></input>

            <button type="submit">Add Todo</button>
        </form>
    );
};

AddTodo = connect()(AddTodo);

export default AddTodo;

Passing the store

Then we can pass a Redux Store into App component with a prop.


import React from 'react';
import ReactDom from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import todoApp from './reducer.js';
import App from './components/App.jsx';

let store = createStore(todoApp);

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('content');
);

results matching ""

    No results matching ""