为什么即使更改了prop对象也无法重新渲染功能组件?

时间:2020-06-28 15:26:56

标签: javascript reactjs use-effect

我有对象构造函数,该对象构造函数具有修改值的方法。 我在react组件中创建了该对象的新实例,并在子组件中呈现了该计数器。当我从child调用方法时,它在对象中得到了更新,但新的更新值未重新呈现。我试过用useEffect来监听道具的变化,但是在值更新时会调用它。但是当我更改子组件的状态时,将显示更新的值。有什么问题。


function Core() {
  this.counter = 0;
}

Core.prototype.handleCounter = function(options) {
  this.counter = this.counter + 1;
  console.log(this.counter);
};

export default function App() {
  let core = new Core();
  return (
    <div className="App">
      <Status counter={core.counter} core={core} />
    </div>
  );
}


App.js

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

export default function Status({ core, counter }) {
  const [localState, setLocal] = React.useState(false);
  function handleButton() {
    core.handleCounter();
    console.log(core.counter);
  }
  return (
    <div>
      <span> counter is {core.counter}</span>
      <button onClick={() => handleButton()}>update object</button>
      <button
        onClick={() => {
          setLocal(!localState);
        }}
      >
        toggle localState
      </button>
    </div>
  );
}

status.js

这是一个代码沙盒网址:https://codesandbox.io/s/quizzical-bash-qh8mn

3 个答案:

答案 0 :(得分:4)

React不知道addCurrency的工作方式;它会看到一个变量Core,它一旦创建就永远不会改变,因此React永远都不知道何时更新。

您似乎开始使用let core = new Core();建立一个中央商店,因此您可能想查看useReducer来处理不断增长的用例:

Core

答案 1 :(得分:0)

当状态改变,道具更改,useSelector返回的值与上次渲染的值不同,useContext返回的值与上次渲染或其他自定义的reducer值不同时,组件会更新。

值更改意味着不同的值,因此不同的原始值或不同的引用。这就是为什么您看到更新状态通常会复制旧状态:{...state,value:newValue}而不是突变状态:state.value=newValue

App组件永远不会重新渲染,因为先前发生重新渲染的原因均未发生。这意味着它永远不会将更改的道具传递给状态。

您可以将本地状态用于计数器,上下文,状态管理器(例如redux或useReducer)。

这是在App中使用本地状态的简单示例

//not using React.memo because counter is the only thing
//  that changes and causes re renders
function Status({ up, counter }) {
  return (
    <div>
      <span> counter is {counter}</span>
      <button onClick={() => up()}>up</button>
    </div>
  );
}
const App = () => {
  const [counter, setCounter] = React.useState(0);
  //not using useCallback because counter is the only thing
  //  that changes and causes re renders
  const up = () => setCounter((c) => c + 1);
  return <Status up={up} counter={counter} />;
};

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


<div id="root"></div>

如果要将逻辑保留在对象中,可以执行以下操作:

function createCore() {
  let listeners = [];
  //create a state container to mutate  to to prevent
  //  stale closures for getState
  const stateContainer = { state: { count: 0 } };
  const trigger = (newState) =>
    listeners.forEach((listener) => listener(newState));
  const addListener = (fn) => {
    listeners.push(fn);
    return () =>
      (listeners = listeners.filter(
        (listener) => listener !== fn
      ));
  };
  const up = () => {
    //mutate state container so getState does
    //  not pass a stale closure
    const state = stateContainer.state;
    stateContainer.state = {
      ...state,
      count: state.count + 1,
    };
    trigger(stateContainer.state);
  };
  return {
    addListener,
    getState: () => stateContainer,
    up,
  };
}
const core = createCore();
function Status() {
  //you could put this in a custom hook called
  //  useCore that returns core and state
  const [state, setState] = React.useState(core.getState());
  React.useEffect(
    //addListener returns a function that will remove the
    //  listener when the component unmounts, this is
    //  automatically used as the cleanup function
    () => core.addListener((state) => setState(state)),
    []
  );
  //custom hook would return [core,state]
  return (
    <div>
      <span> counter is {state.count}</span>
      <button onClick={core.up}>up</button>
    </div>
  );
}
const App = () => {
  return <Status />;
};

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


<div id="root"></div>

答案 2 :(得分:0)

它对我来说是 100% 的工作

你可以像这样改变状态

[JsonIgnore]

或者如果你的 state 是 Array 你可以像这样改变

const [state, setState] = ({})
setState({...state})