Reactjs 组件需要两次单击来更新,而不是一次单击

Reactjs 组件需要两次单击来更新,而不是一次单击,reactjs,event-handling,Reactjs,Event Handling,我有一个按钮,可以将我的笔记存档并发送到单独的存档选项卡。我有以下设置,但是,当我点击归档后,它将需要一次点击,而点击取消归档后,将需要两次点击。我相信这是因为setstate是异步发生的,并且是在update调用之后发生的,但我不确定解决方案。我尝试过为存档之后调用的更新创建一个单独的函数,但没有成功。有什么想法吗 我还不能嵌入图像,所以这里是有问题的代码 完整的代码分解(目前是草率的) 此组件确定要通过道具传递给todolist的类别 import React from 'react';

我有一个按钮,可以将我的笔记存档并发送到单独的存档选项卡。我有以下设置,但是,当我点击归档后,它将需要一次点击,而点击取消归档后,将需要两次点击。我相信这是因为setstate是异步发生的,并且是在update调用之后发生的,但我不确定解决方案。我尝试过为存档之后调用的更新创建一个单独的函数,但没有成功。有什么想法吗

我还不能嵌入图像,所以这里是有问题的代码

完整的代码分解(目前是草率的)

此组件确定要通过道具传递给todolist的类别

import React from 'react';
import TodoListContainer from './todo_list_container';

class Left extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            category: "keeps"
        }
        this.activateKeeps = this.activateKeeps.bind(this);
        this.activateArchive = this.activateArchive.bind(this);
        this.activateTrash = this.activateTrash.bind(this);
    }

    activateKeeps(e){
        e.preventDefault();
        this.setState({ category: "keeps" })
    }

    activateArchive(e){
        e.preventDefault();
        this.setState({ category: "archive" })
    }

    activateTrash(e){
        e.preventDefault();
        this.setState({ category: "trash"})
    }
    

    render(){
        let categ = this.state.category
        return(
            <div>
                <div className="left-bar">
                    <div className="keeps" onClick={this.activateKeeps}>KEEPS</div>
                    <div className="archive" onClick={this.activateArchive}>ARCHIVE</div>
                    <div className="trashed" onClick={this.activateTrash}>TRASH</div>
                </div>
                <TodoListContainer category={categ} />
            </div>
        )
    }
}
容器中的组件将基于通过componentWillReceiveProps中的道具向下推的类别进行渲染(我需要更改它的日期,但它似乎正在工作)

从“/todo_list_item”导入todolitem;
从“./todo_form”导入TodoForm;
从“/Preclick_form”导入预点击;
从“/Nav_container.jsx”导入导航;
类TodoList扩展了React.Component{
建造师(道具){
超级(道具)
此.state={
isEdit:错,
搜索文本:“”,
类别:this.props.category
}
this.handleClick=this.handleClick.bind(this);
this.handleClickOff=this.handleClickOff.bind(this);
this.updateSearch=this.updateSearch.bind(this);
}
componentDidMount(){
this.props.fetchTodos();
}
组件将接收道具(下一步){
this.setState({category:nextrops.category})
}
handleClick(e){
e、 停止传播();
this.setState({isEdit:true});
}
把手舔掉(e){
this.setState({isEdit:false});
}
更新搜索(e){
this.setState({searchText:e});
}
render(){
让searchText=this.state.searchText;
让category=this.state.category;
const{todo,receiveTodo,removeTodo,createTodo,deletetetodo,updateTodo}=this.props;
让我们去做一些事情;
如果(类别==“保留”){
todoItems=todos.map(函数(todo){
if((searchText==''|| todo.title.includes(searchText)| | todo.body.includes(searchText))&&&(todo.archive===false&&todo.trashed==false)){
返回
} 
}
);
}else if(类别==“存档”){
todoItems=todos.map(函数(todo){
if((searchText==''|| todo.title.includes(searchText)| | todo.body.includes(searchText))&&&(todo.archive===true&&todo.trashed==false)){
返回
}
}
);
}否则如果(类别==“垃圾”){
todoItems=todos.map(函数(todo){
if((searchText==“”| | todo.title.includes(searchText)| | todo.body.includes(searchText))&&todo.trashed==true){
返回
}
}
);
}
让它形成;
如果(类别==“垃圾”| |类别==“存档”){
表格=
}否则{
表单=(!this.state.isEdit)?:
}
返回(
//在此处添加handleClickOff或其他解决方案。
{form}
{todoItems}
)}
}
将默认值导出到列表;
从此组件中拉入待办事项列表项

import React from 'react';
import { uniqueId } from '../../util/id_generator';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch, faDrumstickBite, faThList, faArchive } from '@fortawesome/free-solid-svg-icons';
import OutsideClickHandler from 'react-outside-click-handler';



class TodoListItem extends React.Component{
    constructor(props) {
        super(props);
        this.state = {
            todo: this.props.todo,
            color: this.props.todo.color,
            title: this.props.todo.title,
            body: this.props.todo.body,
            archive: false,
            editing: false,
            id: this.props.todo.id,
            optionsOpen: false,
            isHovering: false,
            trashed: this.props.todo.trashed
        }
        this.update = this.update.bind(this);
        this.handleUpdate = this.handleUpdate.bind(this);
        this.archive = this.archive.bind(this);
        this.openOptions = this.openOptions.bind(this);
        this.handleMouseHover = this.handleMouseHover.bind(this);
        this.handleClose = this.handleClose.bind(this);
        this.clickOffNote = this.clickOffNote.bind(this);
    }

    update(property) {
        return e => this.setState({ [property]: e.target.value });
    }

    handleUpdate(e) {
        e.preventDefault();
        const todo = Object.assign({}, this.state, { id: this.state.id });
        this.props.updateTodo(todo);
        this.setState({editing: false});
    }

    openOptions(e) {
        e.preventDefault();
        if (this.state.optionsOpen === false) {
            this.setState({ optionsOpen: true })
        } else {
            this.setState({ optionsOpen: false })
        }
    }

    handleMouseHover() {
        this.setState({isHovering: true});
    }

    handleClose(){
        if(this.state.optionsOpen === false){
            this.setState({isHovering: false})
        }
    }
    
    clickOffNote(){
        this.setState({editing:false, optionsOpen: false, isHovering: false});
    }

    archive() {
        this.setState(prevState => ({
            ...prevState,
            archive: !prevState.archive
        }), () => {
            const todo = Object.assign({}, this.state, { id: this.state.id });
            this.props.updateTodo(todo)
        })
    }

    render(){
        const {todo, removeTodo, deleteTodo, updateTodo} = this.props;
        let color;
        color = this.props.todo.color + "Note";
        let steev;
        let options;
        if(this.state.editing === true){
            steev = <div className={color}>
                        <input className="title" placeholder={this.state.title} onChange={this.update('title')} />
                        <label className="formBodyContainer">
                        <textarea className="formBody" placeholder={this.state.body} onChange={this.update('body')} />
                        </label>
                        <button className="editButton" onClick={this.handleUpdate}>Submit Edit</button>
                    </div>
        } else {
            steev = 
                <div className="test" >
                        <div className={color} onMouseEnter={this.handleMouseHover}
                                            onMouseLeave={this.handleClose}>
                        <div className="noteTitle">{this.state.title}</div>
                        <div className="noteBody">{this.state.body}</div>       
                                {
                                this.state.isHovering &&
                                <div>
                                    <FontAwesomeIcon icon="ellipsis-v" className="noteOptionsToggle" onClick={this.openOptions} />
                                    <FontAwesomeIcon icon="archive" className="archiveOptionsToggle" onClick={this.archive} />
                                </div>
                                } 
                                {
                                this.state.isHovering === false &&
                                <div className="optionsHolder">
                                </div>
                                }     
                            {
                            this.state.optionsOpen &&
                        <OutsideClickHandler onOutsideClick={this.clickOffNote}>   
                            <div className="options">
                                <button className="deleteButton" onClick={() => this.setState({ editing: true, optionsOpen: false })}>Edit Steev</button>
                                <button className="deleteButton" onClick={() => deleteTodo(todo)}>Delete Steev</button>
                                </div>
                        </OutsideClickHandler>
                                } 
                </div>
            </div>
        }

        return(
            <div>
                {steev}
                {options}
            </div>
    )}
}

export default TodoListItem;
从“React”导入React;
从“../../util/id_生成器”导入{uniqueId};
从“@fortawesome/react fontawesome”导入{FontAwesomeIcon}
从“@fortwome/free solid svg icons”导入{faSearch、faDrumstickBite、faThList、faArchive};
从“反应外部点击处理程序”导入外部点击处理程序;
类TodoListItem扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
待办事项:这个。道具。待办事项,
颜色:this.props.todo.color,
标题:this.props.todo.title,
body:this.props.todo.body,
档案:错,
编辑:错,
id:this.props.todo.id,
选项打开:错误,
isHovering:错,
垃圾:这个。道具。待办事项。垃圾
}
this.update=this.update.bind(this);
this.handleUpdate=this.handleUpdate.bind(this);
this.archive=this.archive.bind(this);
this.openOptions=this.openOptions.bind(this);
this.handlemousehave=this.handlemousehave.bind(this);
this.handleClose=this.handleClose.bind(this);
this.clickOffNote=this.clickOffNote.bind(this);
}
更新(属性){
返回e=>this.setState({[property]:e.target.value});
}
手动更新(e){
e、 预防默认值();
const todo=Object.assign({},this.state,{id:this.state.id});
this.props.updateTodo(todo);
this.setState({editing:false});
}
开放选项(e){
e、 预防默认值();
if(this.state.options打开===false){
this.setState({optionsOpen:true})
}否则{
this.setState({optionOpen:false})
}
}
手鼠悬停{
this.setState({isHovering:true});
}
handleClose(){
if(this.state.options打开===false){
this.setState({isHovering:false})
}
}
clickOffNote(){
this.setState({editing:false,optionopen:false,isHovering:false});
}
档案馆(){
this.setState(prevState=>({
…国家,
存档:!prevState.archive
}), () => {
const todo=Object.assign({},this.state,{id:this.state.id});
this.props.updateTodo(todo)
})
}
render(){
const{todo,removeTodo,deletetetodo,up
import TodoListItem from './todo_list_item';
import TodoForm from './todo_form';
import Preclick from './preclick_form';
import Nav from './nav_container.jsx';


class TodoList extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            isEdit: false,
            searchText: '',
            category: this.props.category
        }
        this.handleClick = this.handleClick.bind(this);
        this.handleClickOff = this.handleClickOff.bind(this); 
        this.updateSearch = this.updateSearch.bind(this);
    }

    componentDidMount() {
        this.props.fetchTodos();
    }

    componentWillReceiveProps(nextProps){
        this.setState({category: nextProps.category})
    }

    handleClick(e) {
        e.stopPropagation();
        this.setState({ isEdit: true });
    }

    handleClickOff(e){
        this.setState({ isEdit: false });
    }

    updateSearch(e){
        this.setState({searchText: e});
    }

    render() {
        let searchText = this.state.searchText;
        let category = this.state.category;
        const { todos, receiveTodo, removeTodo, createTodo, deleteTodo, updateTodo } = this.props;
        let todoItems;

        if(category === "keeps"){
            todoItems = todos.map(function(todo){
                if ((searchText === '' || todo.title.includes(searchText) || todo.body.includes(searchText)) && (todo.archive === false && todo.trashed === false)){
                    return <TodoListItem
                        key={`todo-list-item${todo.id}`}
                        todo={todo}
                        receiveTodo={receiveTodo}
                        removeTodo={removeTodo}
                        deleteTodo={deleteTodo}
                        updateTodo={updateTodo}
                        />
                    } 
                }
            );
        } else if(category === "archive"){
            todoItems = todos.map(function (todo) {
                if ((searchText === '' || todo.title.includes(searchText) || todo.body.includes(searchText)) && (todo.archive === true && todo.trashed === false)) {
                    return <TodoListItem
                        key={`todo-list-item${todo.id}`}
                        todo={todo}
                        receiveTodo={receiveTodo}
                        removeTodo={removeTodo}
                        deleteTodo={deleteTodo}
                        updateTodo={updateTodo}
                    />
                }
            }
            );
        } else if(category === "trash"){
            todoItems = todos.map(function (todo) {
                if ((searchText === '' || todo.title.includes(searchText) || todo.body.includes(searchText)) && todo.trashed === true) {
                    return <TodoListItem
                        key={`todo-list-item${todo.id}`}
                        todo={todo}
                        receiveTodo={receiveTodo}
                        removeTodo={removeTodo}
                        deleteTodo={deleteTodo}
                        updateTodo={updateTodo}
                    />
                }
            }
            );
        }

        let form;
        if(category === "trash" || category === "archive"){
            form = <div className="no-form"></div>
        } else {
            form = (!this.state.isEdit) ? <Preclick /> : <TodoForm receiveTodo={receiveTodo} createTodo={createTodo} sessionId={this.props.sessionId} />
        }

        return (
            // add handleClickOff here or another solution.
            <div onClick={this.handleClickOff}> 
                <Nav updateSearch = {this.updateSearch}/> 
                <div className="formToggle" onClick={this.handleClick}>
                    {form}
                </div>
                <div className="notes">
                    {todoItems}
                </div>
        </div>
        )}
}

export default TodoList;
import React from 'react';
import { uniqueId } from '../../util/id_generator';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch, faDrumstickBite, faThList, faArchive } from '@fortawesome/free-solid-svg-icons';
import OutsideClickHandler from 'react-outside-click-handler';



class TodoListItem extends React.Component{
    constructor(props) {
        super(props);
        this.state = {
            todo: this.props.todo,
            color: this.props.todo.color,
            title: this.props.todo.title,
            body: this.props.todo.body,
            archive: false,
            editing: false,
            id: this.props.todo.id,
            optionsOpen: false,
            isHovering: false,
            trashed: this.props.todo.trashed
        }
        this.update = this.update.bind(this);
        this.handleUpdate = this.handleUpdate.bind(this);
        this.archive = this.archive.bind(this);
        this.openOptions = this.openOptions.bind(this);
        this.handleMouseHover = this.handleMouseHover.bind(this);
        this.handleClose = this.handleClose.bind(this);
        this.clickOffNote = this.clickOffNote.bind(this);
    }

    update(property) {
        return e => this.setState({ [property]: e.target.value });
    }

    handleUpdate(e) {
        e.preventDefault();
        const todo = Object.assign({}, this.state, { id: this.state.id });
        this.props.updateTodo(todo);
        this.setState({editing: false});
    }

    openOptions(e) {
        e.preventDefault();
        if (this.state.optionsOpen === false) {
            this.setState({ optionsOpen: true })
        } else {
            this.setState({ optionsOpen: false })
        }
    }

    handleMouseHover() {
        this.setState({isHovering: true});
    }

    handleClose(){
        if(this.state.optionsOpen === false){
            this.setState({isHovering: false})
        }
    }
    
    clickOffNote(){
        this.setState({editing:false, optionsOpen: false, isHovering: false});
    }

    archive() {
        this.setState(prevState => ({
            ...prevState,
            archive: !prevState.archive
        }), () => {
            const todo = Object.assign({}, this.state, { id: this.state.id });
            this.props.updateTodo(todo)
        })
    }

    render(){
        const {todo, removeTodo, deleteTodo, updateTodo} = this.props;
        let color;
        color = this.props.todo.color + "Note";
        let steev;
        let options;
        if(this.state.editing === true){
            steev = <div className={color}>
                        <input className="title" placeholder={this.state.title} onChange={this.update('title')} />
                        <label className="formBodyContainer">
                        <textarea className="formBody" placeholder={this.state.body} onChange={this.update('body')} />
                        </label>
                        <button className="editButton" onClick={this.handleUpdate}>Submit Edit</button>
                    </div>
        } else {
            steev = 
                <div className="test" >
                        <div className={color} onMouseEnter={this.handleMouseHover}
                                            onMouseLeave={this.handleClose}>
                        <div className="noteTitle">{this.state.title}</div>
                        <div className="noteBody">{this.state.body}</div>       
                                {
                                this.state.isHovering &&
                                <div>
                                    <FontAwesomeIcon icon="ellipsis-v" className="noteOptionsToggle" onClick={this.openOptions} />
                                    <FontAwesomeIcon icon="archive" className="archiveOptionsToggle" onClick={this.archive} />
                                </div>
                                } 
                                {
                                this.state.isHovering === false &&
                                <div className="optionsHolder">
                                </div>
                                }     
                            {
                            this.state.optionsOpen &&
                        <OutsideClickHandler onOutsideClick={this.clickOffNote}>   
                            <div className="options">
                                <button className="deleteButton" onClick={() => this.setState({ editing: true, optionsOpen: false })}>Edit Steev</button>
                                <button className="deleteButton" onClick={() => deleteTodo(todo)}>Delete Steev</button>
                                </div>
                        </OutsideClickHandler>
                                } 
                </div>
            </div>
        }

        return(
            <div>
                {steev}
                {options}
            </div>
    )}
}

export default TodoListItem;
handleArchive() {
  this.setState(prevState => ({
    ...prevState,
    archive: !prevState.archive
  }), () => {
    const todo = Object.assign({}, this.state, { id: this.state.id });
    this.props.updateTodo(todo)
  })
}