在函数内部,状态始终相同/初始

时间:2021-03-25 09:32:05

标签: javascript reactjs react-hooks javascript-objects

remove() 函数是从一个对象中调用的。如何在 remove() 函数中获取更新的状态值。

const [InfoBoxPin, setInfoBoxPin] = useState([]) 

const createInfoBoxPin = (descriptions) =>{
       
           var newPin = {
                "location":currentLoc, 
                "addHandler":"mouseover", 
                "infoboxOption": { 
                title: 'Comment', 
                description: "No comment Added",
                actions: [{
                    label:'Remove Pin',
                    eventHandler: function () {
                        remove(newPin.location) //FUNCTION CALLED HERE
                    }
                }] }
                
                }
            setInfoBoxPin((InfoBoxPin)=>[...InfoBoxPin, newPin ]) // UPDATE STATE. Push the above object.
       
    }

const remove = (pos) =>{    
        console.log(InfoBoxPin)    //NEVER GETTING UPDATED STATE HERE.

        //Other codes here......
    }

这是一张 bing 地图信息卡。 Eventhandler 创建一个可以调用任何函数的按钮。

1 个答案:

答案 0 :(得分:1)

问题在于您引用了 remove 函数中的旧状态信息。

当您调用 setInfoBoxPin 时 - InfoBoxPin 的状态已注册以在下一次 UI 呈现时进行更新。这意味着在当前状态下它将是相同的(空)并且所有指向它的链接都将引用一个空数组。

为了解决这个问题,您必须将新状态从视图本身传递给适当的函数。

示例 #1

在这里,我为您创建了一个 CodeSandBox:

https://codesandbox.io/s/react-setstate-example-4d5eg?file=/src/App.js

这里是截取的代码:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [state, setState] = useState({
    InfoBoxPin: [],
    output: []
  });

  const createInfoBoxPin = (descriptions) => {
    var newPin = {
      location: Math.round(Math.random(10) * 1000),
      addHandler: "mouseover",
      infoboxOption: {
        title: "Comment",
        description: "No comment Added",
        actions: [
          {
            label: "Remove Pin",
            eventHandler: removePin
          },
          {
            label: "Update Pin",
            eventHandler: updatePin
          }
        ]
      }
    };
    setState({ ...state, InfoBoxPin: [...state.InfoBoxPin, newPin] });
  };

  const updatePin = (key, state) => {
    var text = `Updating pin with key #${key} - ${state.InfoBoxPin[key].location}`;

    setState({ ...state, output: [...state.output, text] });

    console.log(text, state.InfoBoxPin);
  };

  const removePin = (key, state) => {
    var text = `Removing pin with key #${key} - ${state.InfoBoxPin[key].location}`;

    setState({ ...state, output: [...state.output, text] });

    console.log(text, state.InfoBoxPin);
  };

  return (
    <div className="App">
      <h1>React setState Example</h1>
      <h2>Click on a button to add new Pin</h2>
      <button onClick={createInfoBoxPin}>Add new Pin</button>
      <div>----</div>
      {state.InfoBoxPin.map((pin, pin_key) => {
        return (
          <div key={pin_key}>
            <span>Pin: {pin.location} &nbsp;</span>
            {pin.infoboxOption.actions.map((action, action_key) => {
              return (
                <button
                  key={action_key}
                  onClick={() => action.eventHandler(pin_key, state)}
                >
                  {action.label}
                </button>
              );
            })}
          </div>
        );
      })}
      <h4> OUTPUT </h4>
      <ul style={{ textAlign: "left" }}>
        {state.output.map((txt, i) => {
          return <li key={i}>{txt}</li>;
        })}
      </ul>
    </div>
  );
}

如您所见,我正在为按钮的 InfoBoxPin 事件侦听器的名为 eventHandler 的函数提供一个具有 onclick 值的新状态。

然后在该函数中,我可以使用我需要的状态中的新 InfoBoxPin 值。

示例 2 (ES6)

在这个例子中,我对 App 使用了一些不同的结构 - 使用类 (ES6)

通过为 App 使用一个类,我们可以使用不同的方法来操作 App 状态。

  1. func.bind(this) 可用于初始化时定义的函数
  2. func.call(this) 可用于调用不带参数的动态函数
  3. func.apply(this, [args]) 可用于调用带参数的动态函数

CodeSandBox 链接:

https://codesandbox.io/s/react-setstate-example-using-class-cz2u4?file=/src/App.js

代码片段:

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      InfoBoxPin: [],
      pinName: ""
    };

    /* ------------ method #1 using .bind(this) ------------ */
    this.setPinName = this.setPinName.bind(this);
  }

  remove(key) {
    this.state.InfoBoxPin.splice(key, 1);
    this.setState({ InfoBoxPin: this.state.InfoBoxPin });
  }

  add(pinName) {
    this.state.InfoBoxPin.push(pinName);
    this.setState({ InfoBoxPin: this.state.InfoBoxPin });
  }

  processPinNameAndAdd() {
    let pinName = this.state.pinName.trim();

    if (pinName === "") pinName = Math.round(Math.random() * 1000);

    this.add(pinName);
  }

  setPinName(event) {
    this.setState({ pinName: event.target.value });
  }

  render() {
    return (
      <div className="shopping-list">
        <h1>Pin List</h1>
        <p>Hit "Add New Pin" button.</p>
        <p>(Optional) Provide your own name for the pin</p>

        <input
          onInput={this.setPinName}
          value={this.state.pinName}
          placeholder="Custom name"
        ></input>

        {/* ------------ method #2 using .call(this) ------------ */}
        <button onClick={() => this.processPinNameAndAdd.call(this)}>
          Add new Pin
        </button>

        <ul>
          {this.state.InfoBoxPin.map((pin, pinKey) => {
            return (
              <li key={pinKey}>
                <div>pin: {pin}</div>

                {/* ------------ method #3 using .apply(this, [args]) ------------ */}
                <button onClick={() => this.remove.apply(this, [pinKey])}>
                  Delete Pin
                </button>
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default App;

示例 #3 (ES6) 无法访问创建的元素

此示例将展示如何使用我们自己的参数和来自自动生成的 HTML 元素事件的状态数据处理来自第三方库的回调

CodeSandBox 链接:

https://codesandbox.io/s/react-setstate-example-using-class-no-element-control-lcz5d?file=/src/App.js

代码片段:

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      InfoBoxPin: [],
      lastPinId: 0,
      pinName: ""
    };

    this.setPinName = this.setPinName.bind(this);
  }

  remove(id) {
    let keyToRemove = null;

    this.state.InfoBoxPin.forEach((pin, key) => {
      if (pin.id === id) keyToRemove = key;
    });

    this.state.InfoBoxPin.splice(keyToRemove, 1);
    this.setState({ InfoBoxPin: this.state.InfoBoxPin });
  }

  add(data, id) {
    this.state.InfoBoxPin.push({ id: id, data: data });
    this.setState({
      InfoBoxPin: this.state.InfoBoxPin,
      lastPinId: id
    });
  }

  processPinNameAndAdd() {
    let pinName = this.state.pinName.trim();

    if (pinName === "") pinName = Math.round(Math.random() * 1000);

    var newPinId = this.state.lastPinId + 1;

    var newPin = {
      location: pinName,
      addHandler: "mouseover",
      infoboxOption: {
        title: "Comment",
        description: "No comment Added",
        actions: [
          {
            label: "Remove Pin #" + newPinId,
            // [ES6 class only] using () => func() for callback function
            // By doing so we don't need to use bind,call,apply to pass class ref [this] to a function.
            eventHandler: () => this.remove(newPinId)
          }
        ]
      }
    };

    this.add(newPin, newPinId);
  }

  setPinName(event) {
    this.setState({ pinName: event.target.value });
  }

  render() {
    return (
      <div className="shopping-list">
        <h1>Pin List</h1>
        <p>Hit "Add New Pin" button.</p>
        <p>(Optional) Provide your own name for the pin</p>

        <input onInput={this.setPinName} value={this.state.pinName}></input>

        {/* 
          [ES6 class only] Using {() => func()} for event handler.
          By doing so we don't need to use func.bind(this) for passing class ref at constructor 
        */}
        <button onClick={() => this.processPinNameAndAdd()}>Add new Pin</button>

        <ul>
          {this.state.InfoBoxPin.map((pin, pKey) => {
            return (
              <li key={pKey}>
                <div>pin: {pin.data.location}</div>

                {pin.data.infoboxOption.actions.map((action, aKey) => {
                  return (
                    <button key={aKey} onClick={action.eventHandler}>
                      {action.label}
                    </button>
                  );
                })}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default App;

我在 State 中创建了 lastPinId 条目来跟踪新创建的 Pin 的 ID。 稍后可以使用 Pin id 在 InfoBoxPin 集合中查找所需的 pin 以进行删除。

如何注册您的 eventHandler 最重要的部分是:

eventHandler: () => this.remove(newPinId)

请注意,使用箭头函数 () => func 对于将类引用传递给 remove 函数很重要。