为什么切换todo不能在Redux中工作?

时间:2016-11-09 05:26:45

标签: javascript reactjs react-redux

所以,我一直在研究Dan Abramov的Redux教程,你可以在其中构建一个简单的待办事项应用程序。这是主渲染函数的代码,

const todo = (state, action) => {
    switch(action.type){
        case 'ADD_TODO':
            return {
                id: action.id, 
                text: action.text, 
                completed: false
            }
        case 'TOGGLE_TODO': 
            if(state.id !== action.id){
                return state
            }
            return {...state,
                completed: !state.completed
            }

        default:
            return state
    }
}

const todos = (state = [], action) => {
    switch(action.type){
        case "ADD_TODO": 
            return [
                ...state, 
                todo(undefined, action)
            ]
        case "TOGGLE_TODO": 
            return state.map(t => {
                todo(t, action)
            })
        default: 
            return state;
    }
}

const visibilityFilter = (state = 'SHOW_ALL', action) => {
    switch(action.type){
        case 'SET_VISIBILITY_FILTER': 
            return action.filter
        default:
            return state
    }
}


const todoApp = combineReducers({
    visibilityFilter, 
    todos
});

const store = createStore(todoApp);

const FilterLink = ({
    filter, 
    currentFilter,
    children
}) => {
    if(filter === currentFilter){
        return <span>{children}</span>
    }
    return (
        <a href='#' onClick={e => {
            e.preventDefault();
            store.dispatch({
                type: 'SET_VISIBILITY_FILTER', 
                filter
            })
        }}>
            {children}
        </a>
    )
}

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

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

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
            )
    }
}

let nextTodoId = 0;
class TodoApp extends React.Component {
    render() {
        const {
            todos, 
            visibilityFilter
        } = this.props
        const visibleTodos = getVisibleTodos(todos, visibilityFilter);
        return (
            <div>
                <input ref={text => {
                    this.input = text;
                }} />
                <button onClick={() => {
                    store.dispatch({
                        type:"ADD_TODO", 
                        text: this.input.value, 
                        id: nextTodoId++
                    });
                    this.input.value = '';
                }}>Add a todo
                </button>
                <TodoList todos={visibleTodos} onTodoClick={id => store.dispatch({
                    type: 'TOGGLE_TODO', 
                    id
                })} />
                <p>
                    Show: 
                    {' '}
                    <FilterLink filter='SHOW_ALL' currentFilter={visibilityFilter}>
                        All
                    </FilterLink>
                    {' '}
                    <FilterLink filter='SHOW_COMPLETED' currentFilter={visibilityFilter}>
                        Completed
                    </FilterLink>
                    {' '}
                    <FilterLink filter='SHOW_ACTIVE' currentFilter={visibilityFilter}>
                        Active
                    </FilterLink>
                </p>
            </div>
        )
    }
}



const render = () => {
    console.log(store.getState());
    ReactDOM.render(<TodoApp {...store.getState()}/>, document.getElementById('root'));
}

store.subscribe(render);
render();

当我尝试切换待办事项时,我收到以下错误,

  

index.js:170 Uncaught TypeError:无法读取未定义的属性“id”

现在,当我尝试在尝试切换待办事项时注销this.props.todos时,它返回undefined。这就是我得到错误的原因,因为由于某种原因,没有在click事件上传递this.props.todos。但是,我阅读了课程笔记,并且我有完全相同的代码。我在这做错了什么?我该如何解决?

2 个答案:

答案 0 :(得分:1)

这是你所遵循的例子吗? https://github.com/reactjs/redux/tree/master/examples/todos

不确定是否属于这一点,但在Todo您可能希望删除onClick周围的()

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

答案 1 :(得分:1)

问题在于你的todos reducer中的“TOGGLE_TODO”案例。你有这个代码:

return state.map(t => {todo(t, action)})

括号是不必要的,并使箭头函数期望返回语句。由于没有return语句,返回值是未定义的,因此您将获得一个未定义值的数组。

将其更改为

return state.map(t => todo(t, action));