簡體   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