繁体   English   中英

如何通过 onClick function 在该上下文 onClick function 中创建在该上下文 onClick

[英]How do I pass a React Context's state through an onClick function created in a method inside that context class?

我有一些代码可以将一组 div 元素放置在 CSS 网格中,该网格通过 React 渲染,因为在第一次渲染后速度非常快。 我希望能够通过多个组件与这些元素进行交互,因此我设置了一些上下文来存储网格的 state。 我现在遇到了一个问题,我想在这些网格元素上设置 onClick function 以便在单击它们时更新上下文的 Z9ED39E2EA931586B6A985A6942EF573E。 在这种情况下,我试图通过数组跟踪选定的元素。 但是,我似乎无法在 onClick function 中引用 selectedTiles。 它要么无法编译,要么在运行时崩溃,要么什么都不做。

需要明确的是,如果我删除与 selectedTiles 相关的代码行,它可以正常工作并切换 class 就好了。 我还在构造函数中绑定了 function 。

这是我现在在三个相关文件中的代码。

索引.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import UserInterface from './components/userinterface';
import DisplayGrid from './components/displaygrid';
import {GridContextProvider} from './gridcontext';

ReactDOM.render(
  <React.StrictMode>
    <GridContextProvider>
      <UserInterface />
      <div className="grid-wrapper">
        <DisplayGrid />
      </div>
    </GridContextProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

serviceWorker.unregister();

displaygrid.js

import React from 'react';
import {GridContext} from '../gridcontext'

class DisplayGrid extends React.Component{
    static contextType = GridContext;
    render(){
        const gridData = this.context;
        return (
            <div>
                {gridData.name}<br />
                <div className= "grid-container" style={{gridTemplateColumns: gridData.columns}}>
                    {gridData.tiles}
                </div>
            </div>
        );
    }
}
export default DisplayGrid;

gridcontext.js

import React from "react";

const GridContext = React.createContext();

class GridContextProvider extends React.Component {
    constructor(props) {
        super(props);
        const topGridData = JSON.parse(localStorage.getItem('grids'))[0];
        this.state = this.makeGrid(topGridData.dims[0], topGridData.dims[1], topGridData.dims[2],
            topGridData.visible, topGridData.hex);
        this.selectTile = this.selectTile.bind(this);
        this.makeGrid = this.makeGrid.bind(this);
    }
    selectTile(event) {
        let tilesArray = this.state.selectedTiles;
        if(event.currentTarget.className === "grid-tile"){
            event.currentTarget.className = "grid-tileb";
            tilesArray.push(event.currentTarget.key);
        } else {
            event.currentTarget.className = "grid-tile";
            tilesArray.splice(tilesArray.indexOf(event.currentTarget.key),tilesArray.indexOf(event.currentTarget.key));
        }
    }
    makeGrid(x, y, tilesize, visible, hex){
        let columnStr = "";
        let tileArray = [];
        const widthStr = tilesize.toString() + "px"
        for (let i = 0; i < y; i++) {
            for (let j = 0; j < x; j++) {
                if(i===0) columnStr = columnStr + "auto ";//x loops over columns so this runs once for all columns.
                let div = (
                    <div 
                        key={"x" + j.toString() + "y" + i.toString()}//for example at coordinates 5,6 id is x5y6.  starts at 0.
                        className="grid-tile"
                        style={{
                            width: widthStr,
                            height: widthStr,
                            border: "1px solid rgba(0, 0, 0," + (visible ? "0.6)" : "0.0)")
                        }}
                        onClick={this.selectTile}
                    >
                    </div>
                )
                tileArray.push(div);
            }
        }
        const gridsDataArray = JSON.parse(localStorage.getItem('grids'));
        const index = JSON.parse(localStorage.getItem('currentGrid'));
        return {
            columns: columnStr,
            tiles: tileArray,
            selectedTiles: [],
            name: gridsDataArray[index].name,
            bgurl: gridsDataArray[index].bgurl
        };
    }
    render() {
        return (
            <GridContext.Provider value={{
                columns: this.state.columns,
                tiles: this.state.tiles,
                selectedTiles: this.state.selectedTiles,
                name: this.state.name,
                bgurl: this.state.bgurl,
                setNameBgurl: (newName, newBgurl) => {
                    this.setState({name : newName,
                    bgurl : newBgurl});
                },
                setGrid: (x, y, tilesize, visible, hex) => {
                    this.setState(this.makeGrid(x, y, tilesize, visible, hex));
                }
            }}>
                {this.props.children}
            </GridContext.Provider>
        );
    }
}

export { GridContext, GridContextProvider };

当我点击一个磁贴,指向 gridcontext 的第 15 行时,这段代码在运行时给我一个“TypeError:这是未定义的”崩溃,即let tilesArray = this.state.selectedTiles; 这很奇怪,因为该方法是绑定的。

欢迎其他反馈,因为我是 React 新手,总体而言 javascript 也好不到哪里去; 我不认为我已经完全了解 javascript 如何使用“this”。 我一直在尝试查找教程,但似乎有很多不同的方法可以使用 React,所以很难判断哪些做法是标准的。

selectTile(key, className) {
        let tilesArray = this.state.selectedTiles;
        if(className && className === "grid-tile"){
            // do something 
            // if u want to change element's className. maybe u need ref
        } else {
            // do something
        }
}
    makeGrid(x, y, tilesize, visible, hex){
        ...
        for (let i = 0; i < y; i++) {
            for (let j = 0; j < x; j++) {
                if(i===0) columnStr = columnStr + "auto ";//x loops over columns so this runs once for all columns.
                // u may assume 
                const key = "x" + j.toString() + "y" + i.toString()    
                // const className = "grid-tile"
                // just pass whatever u need. that depends on u
                let div = (
                    <div 
                        key={key}//for example at coordinates 5,6 id is x5y6.  starts at 0.
                        className="grid-tile"
                        style={{
                            width: widthStr,
                            height: widthStr,
                            border: "1px solid rgba(0, 0, 0," + (visible ? "0.6)" : "0.0)")
                        }}
                        // the `key` and the `className` depends on u
                        // 
                        onClick={()=> this.selectTile(key, className)}
                    >
                    </div>
                )
                tileArray.push(div);
            }
        }
}

...

我弄清楚为什么会发生错误。 我在绑定之前在构造函数中使用makeGrid,它引用selectTile,并且“this”的变量指针以某种方式设置为未定义的陈旧变量指针。 将绑定移动到顶部可以修复该错误。

修复该错误后,立即找到第二个错误。 TypeError: tilesArray is undefined这是因为我在 state 完全形成之前引用了 selectTile,它再次留下了一个过时的变量指针。

构造函数现在必须如下所示:

    constructor(props) {
        super(props);
        const topGridData = JSON.parse(localStorage.getItem('grids'))[0];
        this.selectTile = this.selectTile.bind(this);
        this.makeGrid = this.makeGrid.bind(this);
        this.state = {selectedTiles: []}
        this.state = {
            selectedTiles: this.state.selectedTiles,
            ...this.makeGrid(topGridData.dims[0], topGridData.dims[1], topGridData.dims[2],
            topGridData.visible, topGridData.hex)
        }
    }

我立即遇到了第三个问题,即尽管尝试重用变量,但初始的空数组仍被保留为过时的变量指针。 我将 onClick 更改为onClick={(event) => this.selectTile(this.state.selectedTiles, event)}以发送 Z9ED39E2EA931586B6A985A6942EF573E数组,因为我无法通过数组突变直接更改 state)。 我尝试使用onClick={this.selectTile.bind(this, this.state.selectedTiles)} ,结果发现与onClick={this.selectTile}有相同的问题。

事实证明,“key”不是存储在 div 元素中的属性,React 将其拉出并在内部存储以处理列表。 我最终将每个元素的 id 属性设置为相同的字符串,并在 selectTile 中引用该属性来修改数组。

暂无
暂无

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

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