简体   繁体   English

在 Redux 中调度操作时组件不会重新呈现

[英]Component not re-render when dispatch action in Redux

I am learning about Redux and ran into the problem below.我正在学习 Redux 并遇到以下问题。 My component doesn't re-render when dispatch action changeStatus , but works when dispatch actionRemove .我的组件在调度 action changeStatus时不会重新渲染,但在调度actionRemove时工作。 Let me know why?让我知道为什么? Thanks.谢谢。

code here reducer代码在这里reducer

const HandleTaskReducer = (state = initializeState, action) => {

    switch (action.type) {
        case addTask: {
            let list = state.tasks;
            list.push(action.payload);
            localStorage.setItem('todos', JSON.stringify(list));
            return {
                ...state,
                tasks: list
            };
        }
        case completeTask: {
            let list = state.tasks;
            let index = list.findIndex((value => value.id === action.payload));
            if (index > -1) {
                list[index].status = true;
                localStorage.setItem('todos', JSON.stringify(list));
            }
            return {
                ...state,
                tasks: list
            };
        }
        case removeTask: {
            let list = state.tasks;
            let index = list.findIndex((value => value.id === action.payload));
            if (index > -1) {
                list.splice(index, 1);
                localStorage.setItem('todos', JSON.stringify(list));
            }
            return {
                ...state,
                tasks: list
            };
        }
        default: {
            return {...state};
        }
    }
}

this is code in component这是组件中的代码

const TaskItemComponent = props => {
    const className = !props.task.status ? 'item-task bg-grey' : 'item-task bg-grey success';

    const removeTask = () => {
        props.remove(props.task.id);
    }

    const changeStatus = () => {
        props.completeTask(props.task.id);
    }
    return (
        <div className={className}>
            <div className="content-task">
                <h3>{props.task.name}</h3>
                <p>{props.task.description}</p>
            </div>
            <div className="action-task">
                {!props.task.status && <button onClick={changeStatus} className="btn">Complete</button>}
                <button className="btn" onClick={removeTask}>Delete</button>
            </div>
        </div>
    )
}

const mapDispatchToProps = dispatch => {
    return {
        remove: id => {
            dispatch(actionRemove(id))
        },
        completeTask: id => {
            dispatch(actionComplete(id));
            console.log(id)
        }
    }
}

export default connect(null, mapDispatchToProps)(TaskItemComponent);

this is ListTaskComponent这是 ListTaskComponent

import React, {useState, useEffect} from 'react';
import TaskItemComponent from "./TaskItemComponent";
import {connect} from "react-redux";

const ListTaskComponent = props => {
    const [tasks, setTasks] = useState([]);

    useEffect(() => {
        setTasks(props.taskState.tasks);
    }, []);

    const list = tasks.map(value =>
        <TaskItemComponent
            key={value.id.toString()} task={value}/>
    );
    return (
        <section id="wrap-task" className="mt-3">
            {list}
        </section>
    )
}

const mapStateToProps = state => {
    return {
        taskState: state.taskReducer,
    }
}

export default connect(mapStateToProps, null)(ListTaskComponent)

Just check reducer , u did not anything with changeStatus !只需检查reducer ,您对changeStatus没有任何作用!

Try change your ListTaskComponent to below should solve the issue, not need useState just use them directly.尝试将您的ListTaskComponent更改为以下应该可以解决问题,不需要useState直接使用它们。

const ListTaskComponent = props => (
        <section id="wrap-task" className="mt-3">
          {props.taskState.tasks.map(value =>
            <TaskItemComponent key={value.id} task={value}/>
           )}
        </section>
    )

The problem is most likely that you're violate the "single source of truth" principal.问题很可能是您违反了“单一事实来源”原则。

You do this with the following lines of code:您可以使用以下代码行执行此操作:

 const ListTaskComponent = props => { const [tasks, setTasks] = useState([]); useEffect(() => { setTasks(props.taskState.tasks); }, []);

So, what is the plan here?那么,这里的计划是什么? You receive a list of taskState.tasks from the Redux store.您会收到来自 Redux 存储的taskState.tasks列表。 Which is is copied into the components own state.将其复制到自己的组件 state 中。 What happens if taskState.tasks changes?如果taskState.tasks发生变化会发生什么? The ListTaskComponent component will rerender, but since tasks is only set on component mound the value will never update. ListTaskComponent组件将重新呈现,但由于tasks仅在组件堆上设置,因此值永远不会更新。

The problem is easily fixed by removing the local state.通过删除本地 state 可以轻松解决此问题。

const ListTaskComponent = props => {
    const tasks = props.taskState.tasks;

The above should fix the "single source of truth" principal.以上应该解决“单一事实来源”原则。 However if the first thing we do is map the state to a value you might as well move this change into mapStateToProps to simplify the actual component.但是,如果我们做的第一件事是 map 和 state 到一个值,您不妨将此更改移动到mapStateToProps以简化实际组件。

const ListTaskComponent = ({ tasks }) => {
    const list = tasks.map(value => 
        <TaskItemComponent key={value.id.toString()} task={value} />
    );

    return (
        <section id="wrap-task" className="mt-3">{list}</section>
    )
}

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

export default connect(mapStateToProps, null)(ListTaskComponent);

Another issue I can see has to do with the reducer itself.我可以看到的另一个问题与减速器本身有关。 Reducers should not mutate the previous state, yet in your code I can spot mutations to the original state:减速器不应改变以前的 state,但在您的代码中,我可以发现原始 state 的突变:

 let list = state.tasks; list.push(action.payload);
 let list = state.tasks; let index = list.findIndex((value => value.id === action.payload)); if (index > -1) { list[index].status = true;
 let list = state.tasks; let index = list.findIndex((value => value.id === action.payload)); if (index > -1) { list.splice(index, 1);

I suggest reading Writing Reducers which explains the rules that reducers should follow and also gives some examples.我建议阅读Writing Reducers ,它解释了reducer 应该遵循的规则并给出了一些例子。

Coming back to your code you could write the above code blocks in the following manner:回到您的代码,您可以按以下方式编写上述代码块:

let list = [...state.tasks];
list.push(action.payload);
// or
let list = [...state.tasks, action.payload];
let list = [...state.tasks];
let index = list.findIndex((value => value.id === action.payload));
if (index > -1) {
    list[index] = { ...list[index], status: true };
// or
let list = state.tasks.map(value => {
   if (value.id !== action.payload) return value;
   return { ...value, status: true };
});
let list = [...state.tasks];
let index = list.findIndex((value => value.id === action.payload));
if (index > -1) {
    list.splice(index, 1);
// or
let list = state.tasks.filter(value => value.id !== action.payload);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM