反应setState不改变特定属性的状态

时间:2018-10-08 08:54:33

标签: javascript reactjs typescript

我目前所在公司的一位老开发人员最近将尾巴放到两腿之间,不得不执行Typescript / React后逃离,留下一堆乱码。

我现在的问题是,我有这个TypeScript代码,该代码只是从数组中删除一个项目并更改状态:

var currentFiles = this.state.openFiles;
    var index = this.state.openFiles.findIndex((f: IFileModel) => f.fileId == fileId)
    currentFiles.splice(index, 1);
    this.setState({ 
        mode: "gallery",
        openFiles: currentFiles 
    }, () => console.log(this.state.mode));

我的问题是,即使setState 应该这样做,状态也永远不会更新mode。不管我如何进行更改,console.log的显示都是0

即使在渲染函数中设置断点,也向我显示mode0,应该在"gallery"处。

这是初始状态:

this.state = {
            openFiles: [],
            mode: "gallery",
            categories: [],
            galleryState: {}
        }

有什么建议吗?

1 个答案:

答案 0 :(得分:7)

您在评论中说,最近离开公司的一位开发人员已将这段代码留给了您。恐怕他们给您留下的代码违反了React的两个规则::-)

  1. 您不能直接修改状态,包括this.state所引用的对象。您正在使用currentFiles.splice(index, 1)

  2. 您要基于现有状态设置新状态,但不使用setState的回调形式。

要同时解决这两个问题(请参见评论):

// Use the callback form that receives the up-to-date state as a parameter.
this.setState(
    ({openFiles}) => {
        var index = openFiles.findIndex((f: IFileModel) => f.fileId == fileId)
        // (Do you need an `if (index !== -1)` check here?)
        // Create a *new* array without the entry
        var currentFiles = [...openFiles.slice(0, index), ...openFiles.slice(index+1)];
        // Return the new state
        return {
            mode: "gallery",
            openFiles: currentFiles 
        };
    },
    () => console.log(this.state.mode)
);

the state docs中的更多内容。

实时示例:

class Example extends React.Component {
    constructor(...args) {
        super(...args);
        this.removeFileOnClick = this.removeFileOnClick.bind(this);
        this.state = {
            mode: "main",
            openFiles: [
                {fileId: 1, name: "File 1"},
                {fileId: 2, name: "File 2"},
                {fileId: 3, name: "File 3"},
                {fileId: 4, name: "File 4"},
                {fileId: 5, name: "File 5"}
            ]
        };
    }

    removeFileOnClick(e) {
        const fileId = e.currentTarget.getAttribute("data-id");
        this.setState(
            ({openFiles}) => {
                var index = openFiles.findIndex((f) => f.fileId == fileId)
                // (Do you need an `if (index !== -1)` check here?)
                // Create a *new* array without the entry
                var currentFiles = [...openFiles.slice(0, index), ...openFiles.slice(index+1)];
                // Return the new state
                return {
                    mode: "gallery",
                    openFiles: currentFiles 
                };
            },
            () => console.log(this.state.mode)
        );
    }

    render() {
        return ( 
            <div>
                Mode: {this.state.mode}
                <div>
                    OpenFiles ({this.state.openFiles.length}):
                    <div>{this.state.openFiles.map(file =>
                        <div><button data-id={file.fileId} onClick={this.removeFileOnClick}>X</button>{file.name}</div>
                    )}</div>
                </div>
            </div>
        );
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>


旁注:如果您不喜欢此处的双倍价差:

var currentFiles = [...openFiles.slice(0, index), ...openFiles.slice(index+1)];

您可以改为这样:

var currentFiles = openFiles.slice();
currentFiles.splice(index, 1);