为了账号安全,请及时绑定邮箱和手机立即绑定

我如何在 React 功能组件中拥有一个变量的副本,该副本不会更改?

我如何在 React 功能组件中拥有一个变量的副本,该副本不会更改?

撒科打诨 2023-01-06 15:18:51
我const foo: Array<ObjectExt> = useSelector(fooSelector);在功能组件中有一个变量。我想要这个变量的副本,从第一次加载组件开始就不会改变foo。在使用类组件时,我可以简单地拥有const fooCopy = foo.slice();,但这在这里不起作用,因为组件每次都会重新加载并fooCopy发生变化。我如何在功能组件中实现这一点?
查看完整描述

4 回答

?
沧海一幻觉

TA贡献1824条经验 获得超5个赞

只是useState将初始值作为 的副本foo。


const foo : Array<ObjectExt> = useSelector(fooSelector);

const [origFoo] = useState(foo.slice());

一旦origFoo被初始化,它就不会在重新渲染时被重新初始化。如果以后需要更新它的值,可以解构 setter:


const [origFoo, setOrigFoo] = useState(foo);

// ...

if(someCondition) setOrigFoo(foo.slice())

const {useState} = React;


function App() {

  const foo = [new Date().getTime()];

  const [origFoo] = useState(foo.slice());


  // Just so we have a way to force a rerender

  const [count, setCount] = React.useState(0);


  return (

    <div>

      <p>{JSON.stringify(foo)} </p>

      <p>{JSON.stringify(origFoo)}</p>

      <button onClick={() => setCount(count + 1)}>Update</button>

    </div>

  );

}


const rootElement = document.getElementById("root");

ReactDOM.render(

  <App />,

  rootElement

);

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


查看完整回答
反对 回复 2023-01-06
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

一种解决方案是在本地组件状态中设置一个标志。如果该标志为假,则复制该值。否则,不要。



查看完整回答
反对 回复 2023-01-06
?
慕娘9325324

TA贡献1783条经验 获得超4个赞

我想要这个变量的副本,从第一次加载组件开始,当 foo 加载时它不会改变。


当您使用useSelector(selector)react-redux 时,selector每次状态更改时都会运行,如果返回值与上次运行时不同,则 react-redux 将重新渲染组件。


最简单的方法是使用一个只返回第一次调用时获得的值的选择器:


const { Provider, useDispatch, useSelector } = ReactRedux;

const { createStore, applyMiddleware, compose } = Redux;


const initialState = { count: 0 };

//action types

const ADD = 'ADD';

//action creators

const add = () => ({

  type: ADD,

});

const reducer = (state, { type, payload }) => {

  if (type === ADD) {

    return { ...state, count: state.count + 1 };

  }

  return state;

};

//selectors

const selectCount = (state) => state.count;

 //function returning a function

const createSelectInitialCount = () => {

  //initialize NONE when createSelectInitialCount is called

  const NONE = {};

  let last = NONE;

  //return the selector

  return (state) => {

    //check if last value was set

    if (last === NONE) {

      //set last value (only when called the first time)

      last = selectCount(state);

    }

    return last;

  };

};


//creating store with redux dev tools

const composeEnhancers =

  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(

  reducer,

  initialState,

  composeEnhancers(

    applyMiddleware(() => (next) => (action) =>

      next(action)

    )

  )

);

const InitialFoo = React.memo(function InitialFoo(props) {

  const selectInitialCount = React.useMemo(//can also use useCallback

    createSelectInitialCount,//createSelectInitialCount() if you use useCallback

    []

  );

  const foo = useSelector(selectInitialCount);

  return (

    <div>

      <h3>initialfoo</h3>

      <pre>

        {JSON.stringify({ ...props, foo }, undefined, 2)}

      </pre>

    </div>

  );

});


const App = () => {

  const foo = useSelector(selectCount);

  const dispatch = useDispatch();

  const [other, setOther] = React.useState(0);

  const [showFoo, setShowFoo] = React.useState(true);

  const remountFoo = () => {

    setShowFoo(false);

    Promise.resolve().then(() => setShowFoo(true));

  };

  return (

    <div>

      <button onClick={() => dispatch(add())}>

        foo:{foo}

      </button>

      <button onClick={() => setOther((o) => o + 1)}>

        other{other}

      </button>

      <button onClick={remountFoo}>remount Foo</button>

      {showFoo && <InitialFoo other={other} />}

    </div>

  );

};


ReactDOM.render(

  <Provider store={store}>

    <App />

  </Provider>,

  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>

<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>

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

另一种方法是使用 React.memo 和忽略 foo 的自定义比较函数来创建纯组件:

const { Provider, useDispatch, useSelector } = ReactRedux;

const { createStore, applyMiddleware, compose } = Redux;


const initialState = { count: 0 };

//action types

const ADD = 'ADD';

//action creators

const add = () => ({

  type: ADD,

});

const reducer = (state, { type, payload }) => {

  if (type === ADD) {

    return { ...state, count: state.count + 1 };

  }

  return state;

};

//selectors

const selectCount = (state) => state.count;

//creating store with redux dev tools

const composeEnhancers =

  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(

  reducer,

  initialState,

  composeEnhancers(

    applyMiddleware(() => (next) => (action) =>

      next(action)

    )

  )

);

const InitialFoo = React.memo(

  function InitialFoo(props) {

    return (

      <div>

        <h3>initialfoo</h3>

        <pre>{JSON.stringify(props, undefined, 2)}</pre>

      </div>

    );

  },

  //custom compare function when returning false

  //  component will re render

  ({ other }, { other: newOther }) => {

    return other === newOther;

  }

);

const InitialFooContainer = (props) => {

  const foo = useSelector(selectCount);

  //store foo in ref, will never change after mount

  const fooRef = React.useRef(foo);

  const newProps = { ...props, foo: fooRef.current };

  return <InitialFoo {...newProps} />;

};


const App = () => {

  const foo = useSelector(selectCount);

  const dispatch = useDispatch();

  const [other, setOther] = React.useState(0);

  const [showFoo, setShowFoo] = React.useState(true);

  const remountFoo = () => {

    setShowFoo(false);

    Promise.resolve().then(() => setShowFoo(true));

  };

  return (

    <div>

      <button onClick={() => dispatch(add())}>

        foo:{foo}

      </button>

      <button onClick={() => setOther((o) => o + 1)}>

        other{other}

      </button>

      <button onClick={remountFoo}>remount Foo</button>

      {showFoo && (

        <InitialFooContainer foo={foo} other={other} />

      )}

    </div>

  );

};


ReactDOM.render(

  <Provider store={store}>

    <App />

  </Provider>,

  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>

<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>

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


查看完整回答
反对 回复 2023-01-06
?
富国沪深

TA贡献1790条经验 获得超9个赞

我用来完成此功能的解决方案是复制foo,例如商店中的东西initialFoo,然后在需要的组件中挑选它。



查看完整回答
反对 回复 2023-01-06
  • 4 回答
  • 0 关注
  • 100 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信