当我有没有子组件的组件时,我所有的动作都可以正常工作,但是,当我添加组件时,当调度deleteTodo动作时,TodoList /不会改变,并且它现在也循环呈现,与更改TodoList的动作相同状态“过滤器”。我发现可能是因为变异,但我什至没有碰到我的减速器。其他动作不会发生。感谢任何帮助,谢谢!
TodoList类:
import React, { Component } from 'react';
import { Container, ListGroup, ListGroupItem, Button, Input } from 'reactstrap';
import { CSSTransition, TransitionGroup } from 'react-transition-group'
import { connect } from 'react-redux';
import { getTodos, addTodo, deleteTodo, editTodo, updateTime, completeTodo } from '../actions/todoActions';
import 'bootstrap/dist/css/bootstrap.css'
import moment from 'moment'
import Todo from './Todo'
import 'react-datepicker/dist/react-datepicker.css';
import AddTodo from './AddTodo'
class TodoList extends Component {
constructor() {
super();
this.state = {
addForm: false,
filter: 'all'
}
}
componentDidMount() {
const tick = 1000;
setInterval(this.props.updateTime, tick)
// this.props.getTodos();
}
editHandler = (id, name, description, shouldCompleteAt, importance, completedAt) => {
this.clearForm();
this.setState({
id,
name,
description,
shouldCompleteAt: shouldCompleteAt || moment(),
importance,
completedAt
})
}
deleteHandler = (id) => {
console.log('22')
this.props.deleteTodo(id)
}
handleAdd = () => {
this.clearForm();
this.setState({
addForm: true
})
}
renderForm = () => {
if (this.state.addForm) {
return <AddTodo clear={this.clearForm} />
}
}
handleComplete = (id) => {
this.props.completeTodo(id);
}
handleFilterChange = e => {
console.log(e)
e.preventDefault();
this.setState({
filter: e.target.value
})
}
clearForm = () => {
this.setState({
addForm: false
})
}
renderTime = (shouldCompleteAt, completedAt) => {
if (shouldCompleteAt === null || shouldCompleteAt === undefined || !moment(shouldCompleteAt).isValid()) {
return null
} else {
return <div>Complete till <span className={"todo-complete-to " + (completedAt ? "green" : this.props.todos.time > moment(shouldCompleteAt) ? "red" : "black")}>{moment(shouldCompleteAt).format('Do MMMM h:mm')}</span></div>
}
}
renderTodo = (todo) => {
if (this.state.filter !== 'all' && todo.importance !== this.state.filter) {
return null
} else if (this.state.id !== undefined && this.state.id === todo.id) {
return <AddTodo key={todo.id} clearForm={this.clearForm} todo={todo} />
} else {
return <Todo key={todo.id} todo={todo} time={this.props.time} />
}
}
render() {
console.log('1')
return (
<Container>
<Input type="select"
value={this.state.filter}
onChange={this.handleFilterChange}>
<option>all</option>
<option>normal</option>
<option>important</option>
<option>very important</option>
</Input>
<ListGroup>
<TransitionGroup className="todo-list">
{this.props.todos.todos.map((todo) => {
return (this.renderTodo(todo))
})}
</TransitionGroup>
</ListGroup>
{this.renderForm(this.state.addForm, null, "add")}
<Button
className="addButton"
color="dark"
style={{ marginBottom: '2rem' }}
onClick={() => {
this.handleAdd();
}}
>
Add Todo
</Button>
</Container>
)
}
}
const mapStateToProps = (state) => ({
todos: state.todos,
time: state.todos.time,
filter: state.todos.filter
})
export default connect(mapStateToProps, { getTodos, deleteTodo, addTodo, editTodo, updateTime, completeTodo })(TodoList);
Todo类:
import React, { Component } from 'react';
import { ListGroupItem, Button } from 'reactstrap';
import { connect } from "react-redux";
import { CSSTransition } from 'react-transition-group'
import {deleteTodo, editTodo, completeTodo } from '../actions/todoActions';
import moment from 'moment';
import AddTodo from './AddTodo'
class Todo extends Component {
constructor() {
super();
this.state = {
edit: false
}
}
editHandler = () => {
this.setState({
edit: true
})
}
deleteHandler = (id) => {
this.props.deleteTodo(id)
this.forceUpdate()
}
handleAdd = () => {
this.clearForm();
this.setState({
addForm: true
})
}
renderForm = (addForm = false, todo = {}, type) => {
if (type === 'add' && addForm) {
return <AddTodo clearForm={this.clearForm} />
} else if (type === 'edit' && addForm) {
return <AddTodo clearForm={this.clearForm} todo={todo} />
}
}
handleComplete = (id) => {
this.props.completeTodo(id);
}
renderTime = (shouldCompleteAt, completedAt) => {
if (shouldCompleteAt === null || shouldCompleteAt === undefined || !moment(shouldCompleteAt).isValid()) {
return null
} else {
return <div>Complete till <span className={"todo-complete-to" + ' ' + (completedAt ? "green" : this.props.time > moment(shouldCompleteAt) ? "red" : "black")}>{moment(shouldCompleteAt).format('Do MMMM h:mm')}</span></div>
}
}
clearEdit = () => {
this.setState({
edit: false
})
}
renderTodo = () => {
const {
name,
description,
importance,
shouldCompleteAt,
completedAt,
id
} = this.props.todo;
if (this.state.edit) {
console.log('1')
return <AddTodo todo={this.props.todo} clear={this.clearEdit} />
} else {
return <CSSTransition key={id} timeout={900} classNames="fade">
<ListGroupItem>
<div className="todo-header">
<div className="todo-title">
{name}
</div>
<div className="todo-importance">
{importance}
</div>
</div>
<hr />
<div className="todo-body">
{description}
</div>
<hr />
<div className="todo-footer">
<Button
className="remove-btn" color="danger"
size="sm"
onClick={
() => {
this.deleteHandler(id)
}
}>×</Button>
<div className="todo-time">
{this.renderTime(shouldCompleteAt, completedAt)}
</div>
<div className="change-buttons">
<Button
className="edit-btn" color="primary"
size="sm"
onClick={
() => {
this.editHandler()
}
}>Edit</Button>
<Button
className="complete-btn"
color="success"
size="sm"
disabled={completedAt !== null && completedAt !== undefined}
onClick={
() => {
this.handleComplete(id)
}
}
>Complete</Button>
</div>
</div>
</ListGroupItem>
</CSSTransition>
}
}
render() {
return (
this.renderTodo()
);
}
}
export default connect(null, { deleteTodo, editTodo, completeTodo })(Todo);
减速器:
import { GET_TODOS, DELETE_TODO, ADD_TODO, COMPLETE_TODO, EDIT_TODO, UPDATE_TIME } from '../actions/types';
import { classNamesShape } from 'reactstrap';
import moment from 'moment'
const initialState = {
todos: [
{ id: 1, name: 'Homework', description: 'Do homework', importance: 'normal', shouldCompleteAt: moment(), completedAt: null },
{ id: 2, name: 'Milk', description: 'Buy milk', importance: 'important', shouldCompleteAt: moment(), completedAt: null },
{ id: 3, name: 'Music', description: 'Listen music', importance: 'very important', shouldCompleteAt: moment(), completedAt: null },
],
time: moment(),
filter: 'all'
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_TODOS:
return {
...state
}
case DELETE_TODO:
console.log(action.payload)
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload]
}
case COMPLETE_TODO:
return {
...state,
todos: state.todos.map(todo => {
if (todo.id === action.payload) {
todo.completedAt = moment();
}
return todo;
})
}
case EDIT_TODO:
return {
...state,
todos: state.todos.map(todo => {
if (todo.id === action.payload.id) {
todo = action.payload
}
return todo;
})
}
case UPDATE_TIME:
return {
...state,
time: moment()
}
default:
return state;
}
}
UPD: 这是AddTodo类,所有调度都在这里工作:
import React, { Component } from 'react';
import { ListGroupItem, Button, Input } from 'reactstrap';
import { CSSTransition } from 'react-transition-group'
import { connect } from 'react-redux';
import { addTodo, editTodo } from '../actions/todoActions';
import 'bootstrap/dist/css/bootstrap.css'
import uuid from 'uuid'
import moment from 'moment'
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
class AddTodo extends Component {
constructor() {
super();
this.state = {
id: -1,
name: '',
description: '',
shouldCompleteAt: null,
importance: 'normal',
completedAt: null
}
}
componentDidMount() {
if (this.props.todo) {
this.setState({
...this.props.todo
})
}
}
deleteHandler = () => {
this.props.clear();
}
handleNameChange = (e) => {
e.preventDefault();
this.setState({
name: e.target.value
})
}
handleImportanceChange = (e) => {
e.preventDefault();
this.setState({
importance: e.target.value
})
}
handleDescriptionChange = (e) => {
e.preventDefault();
this.setState({
description: e.target.value
})
}
handleDateChange = (e) => {
this.setState({
shouldCompleteAt: moment(e).isValid() ? moment(e) : null
})
}
handleSubmit = () => {
const todo = {
id: this.state.id,
name: this.state.name,
description: this.state.description,
shouldCompleteAt: moment(this.state.shouldCompleteAt),
importance: this.state.importance,
completedAt: this.completedAt
}
if (this.props.todo) {
this.props.editTodo(todo)
} else {
todo.id = uuid()
this.props.addTodo(todo)
}
this.props.clear()
}
renderDatepicker = () => {
if (this.state.shouldCompleteAt !== null && this.state.shouldCompleteAt !== undefined) {
return (<DatePicker className="todo-complete-to"
selected={moment(this.state.shouldCompleteAt || '')}
onChange={this.handleDateChange}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={5}
dateFormat="DD/MM/YYYY HH:mm"
timeCaption="time"
/>)
} else {
return (<DatePicker className="todo-complete-to"
onChange={this.handleDateChange}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={5}
dateFormat="DD/MM/YYYY HH:mm"
timeCaption="time"
/>)
}
}
render() {
return (
<CSSTransition timeout={900} classNames="fade">
<ListGroupItem>
<div className="todo-header">
<div className="todo-title">
<Input type="text"
value={this.state.name}
onChange={this.handleNameChange} />
</div>
<div className="todo-importance">
<Input type="select"
value={this.state.importance}
onChange={this.handleImportanceChange}>
<option>normal</option>
<option>important</option>
<option>very important</option>
</Input>
</div>
</div>
<hr />
<div className="todo-body">
<Input type="textarea" value={this.state.description} onChange={this.handleDescriptionChange} />
</div>
<hr />
<div className="todo-footer">
<Button
className="remove-btn" color="danger"
size="sm"
onClick={
() => {
this.deleteHandler(this.state.id)
}
}>×</Button>
<div className="todo-time">
Complete till
{this.renderDatepicker()}
</div>
<div className="change-buttons">
<Button
className="complete-btn" color="success"
size="sm"
onClick={
() => {
this.handleSubmit()
}
}>Submit</Button>
</div>
</div>
</ListGroupItem>
</CSSTransition>
)
}
}
export default connect(null, { addTodo, editTodo })(AddTodo);
UPD :: 好的,我发现该循环是由我的setInterval引起的:) 但是删除元素后,List仍然不会重新渲染
UPD:
case DELETE_TODO:
console.log(action.payload)
console.log(state.todos[0].id)
console.log(state.todos[0].id === action.payload)
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}
删除第一个元素 结果是
todoReducer.js:23
ab6f802d-be6e-4b7c-8480-16ad4073630d
todoReducer.js:24
ab6f802d-be6e-4b7c-8480-16ad4073630d
todoReducer.js:25
true
完整的项目,如果有帮助的话: https://github.com/MikluhaMaclay/todo-react https://codesandbox.io/s/github/MikluhaMaclay/todo-react
UPD ::: 对我来说是意外的,但是从render删除ListGroup和TransitionGroup确实解决了我的问题!
答案 0 :(得分:-1)
在您的TodoList类中尝试以下操作:
将您的constructor
更新为使用todos
开头的props
:
constructor(props) {
super(props)
this.state = {
todos: props.todos,
addForm: false,
filter: 'all'
}
}
要处理redux
state
的更改,请在componentDidMount
之后添加:
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.todos.length !== prevState.todos.length){
return { todos: nextProps.todos};
}
else return null;
}
还使用todos
中的state
:
<TransitionGroup className="todo-list">
{this.state.todos.map((todo) => {
return (this.renderTodo(todo))
})}
</TransitionGroup>