开发者问题收集

React 获取空状态

2022-10-23
77

我正在尝试创建一个高级正则表达式搜索应用程序,以便我搜索某些内容并将其替换到其他行中,但我正在努力完成一项更简单的任务:无法删除规则,因为 rules 状态似乎为空:

export default function App() {
  const [displayNewRule, setDisplayNewRule] = useState(false);
  const [replaceType, setReplaceType] = useState("line");
  const [rules, setRules] = useState([]);

  function handleClick() {
    setDisplayNewRule(true);
  }

  function handleSubmit(e) {
    e.preventDefault();

    const rule = {
      name: e.target.name.value,
      sourceSearchPattern: e.target.sourceSearchPattern.value,
      replaceType,
      value: e.target.value.value
    };

    if (replaceType === "occurrence") {
      rule.targetSearchPattern = e.target.targetSearchPattern.value;
      rule.location = e.location.value;
    }

    let $rules = rules;
    $rules.push(rule);
    setRules($rules);

    e.target.reset();
    handleReset();
  }

  function handleReset() {
    setDisplayNewRule(false);
  }

  function deleteRule(i) {
    let $rules = rules;
    $rules.splice(i, 1);
    setRules($rules);

    // this gets an empty rules
  }

  return (
    <div className="App">
      <form
        onSubmit={handleSubmit}
        onReset={handleReset}
        style={{ display: displayNewRule || "none" }}
      >
        <h3>Create new rule</h3>

        <div className="input-group">
          <label htmlFor="name">Name:</label>
          <input type="text" id="name" name="name" required />
        </div>

        <div className="input-group">
          <label htmlFor="sourceSearchPattern">Source search pattern:</label>
          <input
            type="text"
            id="sourceSearchPattern"
            name="sourceSearchPattern"
            required
          />
        </div>

        <div className="input-group">
          <label htmlFor="replaceType">Replace type:</label>
          <select
            id="replaceType"
            name="replaceType"
            onChange={(e) => {
              setReplaceType(e.target.value);
            }}
            required
          >
            <option value="">Select item</option>
            <option value="line">Replace line</option>
            <option value="occurrence">Replace occurrence</option>
          </select>
        </div>

        {replaceType === "occurrence" && (
          <div className="input-group">
            <label htmlFor="targetSearchPattern">Target search pattern:</label>
            <input
              type="text"
              id="targetSearchPattern"
              name="targetSearchPattern"
              required
            />
          </div>
        )}

        <div className="input-group">
          <label htmlFor="value">Value:</label>
          <input type="text" id="value" name="value" required />
        </div>

        {replaceType === "occurrence" && (
          <div className="input-group">
            Occurrence location:
            <div className="option-group">
              <input
                type="radio"
                id="next-occurrence"
                name="location"
                value="next"
                required
              />
              <label htmlFor="next-occurrence">Next occurrence</label>
            </div>
            <div className="option-group">
              <input
                type="radio"
                id="previous-occurrence"
                name="location"
                value="previous"
              />
              <label htmlFor="previous-occurrence">Previous occurrence</label>
            </div>
          </div>
        )}

        <button type="submit">Submit</button>
        <button type="reset">Cancel</button>
      </form>

      <button onClick={handleClick}>Add rule</button>

      <div className="rules">
        <h3>Rules</h3>
        {rules.map((rule, i) => ( // These rules are being displayed.
          <div className="rule" key={i + 1}>
            <h5>
              {rule.name}
              <button
                onClick={() => {
                  deleteRule(i);
                }}
              >
                Delete rule
              </button>
            </h5>
            <p>Replace type: {rule.replaceType}</p>
            <p>Source search pattern: {rule.sourceSearchPattern}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

有什么线索吗?

2个回答

而不是

let $rules = rules;
$rules.push(rule);
setRules($rules);

尝试

setRules(oldRules => [...oldRules, rule]);

您正在当前代码中改变状态,这是一个巨大的反模式。我不确定它是否导致了您看到的问题,但更改为此模式将使调试和消除变量变得更容易。还要更改您的 deleteRule 函数,使其同样不会改变状态(使用 [...] 规则复制状态并在设置新规则之前对其进行操作)。

Nathan
2022-10-23

tl;dr - 您的组件 根本不 重新渲染。

更多说明: $rules 保留 state 对象 rules 的引用(它是同一个实例)。调用 setRules 时, 浅比较 会进入并阻止您更新组件(由于直接 突变 ,新值与当前值 完全相同 )。

有人会问,为什么 浅比较 会阻止组件重新渲染,因为它仅适用于 基元 (数组 不是 )。问题是 浅比较 仍然适用于持有 相同引用 的对象。

const array = [];
array === array // true;

带有分步描述的代码:

function handleSubmit() {
  let $rules = rules; // $rules holds the reference and is the same instance as rules
  $rules.push(rule); // directly updated state (mutated) 
                     // value of $rules and rules is again exactly the same!

  setRules($rules); // the component is not even updated because of the 
                    // shallow comparison - $rules and rules is the same instance
                    // with exactly the same value
}

解决方案是避免直接变异。

setRules((oldRules) => [...oldRules, rule]); 如@Nathan 上面发布的那样。

kind user
2022-10-23