LoginSignup
5
4

More than 3 years have passed since last update.

React Redux: 公式サイト「Basic Tutorial」の作例をフックで書き替える

Last updated at Posted at 2020-05-01

React 16.8から、関数コンポーネントにReactの機能を加えるフック(hook)が採り入れられました。足並みを揃えてReact Redux 7.1.0に備わったのが、Reduxにつなげるフックです。React Redux公式「Basic Tutorial」の作例「Todo App with Redux」は、残念ながらReact 16.4.2でつくられ、どちらのフックも使われていません。そこで、フック対応に書き替えてみようというのが本稿のお題です。

2005001_01.png
>> Todo App with Redux

クラスコンポーネントを関数コンポーネントに書き替える

まず、モジュールsrc/components/AddTodo.jsは、コンポーネントに状態(state)をもたせるため、クラスで定められています。ReactのフックuseState()を使って、関数コンポーネントに直しましょう(「React Hooks: クラスのコンポーネントをuseState()で関数に書き替える」参照)。関数コンポーネントの中ではthis参照がなくなることにご注目ください。

src/components/AddTodo.js
// import React from 'react';
import React, { useState } from 'react';

// class AddTodo extends React.Component {
const AddTodo = ({ addTodo }) => {
    /* constructor(props) {
        super(props);
        this.state = { input: '' };
    } */
    const [input, setInput] = useState('');

    // updateInput = input => {
    const updateInput = input => {
        // this.setState({ input });
        setInput(input);
    };

    // handleAddTodo = () => {
    const handleAddTodo = () => {
        // this.props.addTodo(this.state.input);
        addTodo(input);
        // this.setState({ input: '' });
        setInput('');
    };

    // render() {
    return (
        <div>
            <input
                // onChange={e => this.updateInput(e.target.value)}
                onChange={e => updateInput(e.target.value)}
                // value={this.state.input}
                value={input}
            />
            {/* <button className='add-todo' onClick={this.handleAddTodo}> */}
            <button className='add-todo' onClick={handleAddTodo}>
                Add Todo
            </button>
        </div>
    );
    // }
};

useDispatch()を使う

つぎに、React Reduxのフックを使ってゆきます。connect()関数の第2引数に与え、アクションを発行するdispatch()につなげるフックがuseDispatch()です。アクションクリエーター(addTodo)は、フックから返された関数(dispatch())で呼び出します。

src/components/AddTodo.js
// import { connect } from 'react-redux';
import { useDispatch } from 'react-redux';

// const AddTodo = ({ addTodo }) => {
const AddTodo = () => {

    const dispatch = useDispatch();

    const handleAddTodo = () => {
        // addTodo(input);
        dispatch(addTodo(input));
    };

};

/* export default connect(
    null,
    { addTodo }
)(AddTodo); */
export default AddTodo;

これでモジュールsrc/components/AddTodo.jsが純粋な関数コンポーネントになりました。src/components/Todo.jsも、同じようにuseDispatch()connect()のラップが外せます。

src/components/Todo.js
// import { connect } from 'react-redux';
import { useDispatch } from 'react-redux';
import cx from 'classnames';
import { toggleTodo } from '../redux/actions';

// const Todo = ({ todo, toggleTodo }) => (
const Todo = ({ todo }) => {
    const dispatch = useDispatch();
    return (
        // <li className='todo-item' onClick={() => toggleTodo(todo.id)}>
        <li className='todo-item' onClick={() => dispatch(toggleTodo(todo.id))}>

        </li>
    );
}

/* export default connect(
    null,
    { toggleTodo }
)(Todo); */
export default Todo;

useSelector()を使う

connect()の第1引数(mapStateToProps)が担う、Storeから状態(state)を取り出すフックがuseSelector()です。渡すのは関数で、引数(state)の状態から処理した値を返します。モジュールsrc/components/TodoList.jsは、つぎのように書き替えればよいでしょう。

src/components/TodoList.js
// import { connect } from 'react-redux';
import { useSelector } from 'react-redux';

// const TodoList = ({ todos }) => (
const TodoList = () => {
    const todos = useSelector(state => {
        const visibilityFilter = state.visibilityFilter;
        const todos = getTodosByVisibilityFilter(state, visibilityFilter);
        return todos;
    });

}

/* const mapStateToProps = state => {
    const { visibilityFilter } = state;
    const todos = getTodosByVisibilityFilter(state, visibilityFilter);
    return { todos };
};
export default connect(mapStateToProps)(TodoList); */
export default TodoList;

残るモジュールsrc/components/VisibilityFilters.jsは、connect()にふたつの引数が与えられています。したがって、フックもuseSelector()useDispatch()をともに使わなければなりません。

src/components/VisibilityFilters.js
// import { connect } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';

// const VisibilityFilters = ({ activeFilter, setFilter }) => {
const VisibilityFilters = () => {
    const activeFilter = useSelector(state => state.visibilityFilter);
    const dispatch = useDispatch();
    return (
        <div className='visibility-filters'>
            {Object.keys(VISIBILITY_FILTERS).map(filterKey => {
                const currentFilter = VISIBILITY_FILTERS[filterKey];
                return (
                    <span

                        onClick={() => {
                            // setFilter(currentFilter);
                            dispatch(setFilter(currentFilter));
                        }}
                    >
                        {currentFilter}
                    </span>
                );
            })}
        </div>
    );
};

/* const mapStateToProps = state => {
    return { activeFilter: state.visibilityFilter };
};
export default connect(
    mapStateToProps,
    { setFilter }
)(VisibilityFilters); */
export default VisibilityFilters;

以上でReact Redux公式「Basic Tutorial」の作例が、フックを使った純粋な関数コンポーネントに書き直せました。でき上がりはCodeSandboxに「Todo App with Redux using Hooks」として公開しました。

2005001_02.png
>> Todo App with Redux using Hooks

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4