猿问

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

const foo: Array<ObjectExt> = useSelector(fooSelector);在功能组件中有一个变量。我想要这个变量的副本,从第一次加载组件开始就不会改变foo

在使用类组件时,我可以简单地拥有const fooCopy = foo.slice();,但这在这里不起作用,因为组件每次都会重新加载并fooCopy发生变化。

我如何在功能组件中实现这一点?


撒科打诨
浏览 171回答 4
4回答

沧海一幻觉

只是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() {&nbsp; const foo = [new Date().getTime()];&nbsp; const [origFoo] = useState(foo.slice());&nbsp; // Just so we have a way to force a rerender&nbsp; const [count, setCount] = React.useState(0);&nbsp; return (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; <p>{JSON.stringify(foo)} </p>&nbsp; &nbsp; &nbsp; <p>{JSON.stringify(origFoo)}</p>&nbsp; &nbsp; &nbsp; <button onClick={() => setCount(count + 1)}>Update</button>&nbsp; &nbsp; </div>&nbsp; );}const rootElement = document.getElementById("root");ReactDOM.render(&nbsp; <App />,&nbsp; 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>

料青山看我应如是

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

慕娘9325324

我想要这个变量的副本,从第一次加载组件开始,当 foo 加载时它不会改变。当您使用useSelector(selector)react-redux 时,selector每次状态更改时都会运行,如果返回值与上次运行时不同,则 react-redux 将重新渲染组件。最简单的方法是使用一个只返回第一次调用时获得的值的选择器:const { Provider, useDispatch, useSelector } = ReactRedux;const { createStore, applyMiddleware, compose } = Redux;const initialState = { count: 0 };//action typesconst ADD = 'ADD';//action creatorsconst add = () => ({&nbsp; type: ADD,});const reducer = (state, { type, payload }) => {&nbsp; if (type === ADD) {&nbsp; &nbsp; return { ...state, count: state.count + 1 };&nbsp; }&nbsp; return state;};//selectorsconst selectCount = (state) => state.count;&nbsp;//function returning a functionconst createSelectInitialCount = () => {&nbsp; //initialize NONE when createSelectInitialCount is called&nbsp; const NONE = {};&nbsp; let last = NONE;&nbsp; //return the selector&nbsp; return (state) => {&nbsp; &nbsp; //check if last value was set&nbsp; &nbsp; if (last === NONE) {&nbsp; &nbsp; &nbsp; //set last value (only when called the first time)&nbsp; &nbsp; &nbsp; last = selectCount(state);&nbsp; &nbsp; }&nbsp; &nbsp; return last;&nbsp; };};//creating store with redux dev toolsconst composeEnhancers =&nbsp; window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const store = createStore(&nbsp; reducer,&nbsp; initialState,&nbsp; composeEnhancers(&nbsp; &nbsp; applyMiddleware(() => (next) => (action) =>&nbsp; &nbsp; &nbsp; next(action)&nbsp; &nbsp; )&nbsp; ));const InitialFoo = React.memo(function InitialFoo(props) {&nbsp; const selectInitialCount = React.useMemo(//can also use useCallback&nbsp; &nbsp; createSelectInitialCount,//createSelectInitialCount() if you use useCallback&nbsp; &nbsp; []&nbsp; );&nbsp; const foo = useSelector(selectInitialCount);&nbsp; return (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; <h3>initialfoo</h3>&nbsp; &nbsp; &nbsp; <pre>&nbsp; &nbsp; &nbsp; &nbsp; {JSON.stringify({ ...props, foo }, undefined, 2)}&nbsp; &nbsp; &nbsp; </pre>&nbsp; &nbsp; </div>&nbsp; );});const App = () => {&nbsp; const foo = useSelector(selectCount);&nbsp; const dispatch = useDispatch();&nbsp; const [other, setOther] = React.useState(0);&nbsp; const [showFoo, setShowFoo] = React.useState(true);&nbsp; const remountFoo = () => {&nbsp; &nbsp; setShowFoo(false);&nbsp; &nbsp; Promise.resolve().then(() => setShowFoo(true));&nbsp; };&nbsp; return (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; <button onClick={() => dispatch(add())}>&nbsp; &nbsp; &nbsp; &nbsp; foo:{foo}&nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; <button onClick={() => setOther((o) => o + 1)}>&nbsp; &nbsp; &nbsp; &nbsp; other{other}&nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; <button onClick={remountFoo}>remount Foo</button>&nbsp; &nbsp; &nbsp; {showFoo && <InitialFoo other={other} />}&nbsp; &nbsp; </div>&nbsp; );};ReactDOM.render(&nbsp; <Provider store={store}>&nbsp; &nbsp; <App />&nbsp; </Provider>,&nbsp; 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 typesconst ADD = 'ADD';//action creatorsconst add = () => ({&nbsp; type: ADD,});const reducer = (state, { type, payload }) => {&nbsp; if (type === ADD) {&nbsp; &nbsp; return { ...state, count: state.count + 1 };&nbsp; }&nbsp; return state;};//selectorsconst selectCount = (state) => state.count;//creating store with redux dev toolsconst composeEnhancers =&nbsp; window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;const store = createStore(&nbsp; reducer,&nbsp; initialState,&nbsp; composeEnhancers(&nbsp; &nbsp; applyMiddleware(() => (next) => (action) =>&nbsp; &nbsp; &nbsp; next(action)&nbsp; &nbsp; )&nbsp; ));const InitialFoo = React.memo(&nbsp; function InitialFoo(props) {&nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; <h3>initialfoo</h3>&nbsp; &nbsp; &nbsp; &nbsp; <pre>{JSON.stringify(props, undefined, 2)}</pre>&nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; );&nbsp; },&nbsp; //custom compare function when returning false&nbsp; //&nbsp; component will re render&nbsp; ({ other }, { other: newOther }) => {&nbsp; &nbsp; return other === newOther;&nbsp; });const InitialFooContainer = (props) => {&nbsp; const foo = useSelector(selectCount);&nbsp; //store foo in ref, will never change after mount&nbsp; const fooRef = React.useRef(foo);&nbsp; const newProps = { ...props, foo: fooRef.current };&nbsp; return <InitialFoo {...newProps} />;};const App = () => {&nbsp; const foo = useSelector(selectCount);&nbsp; const dispatch = useDispatch();&nbsp; const [other, setOther] = React.useState(0);&nbsp; const [showFoo, setShowFoo] = React.useState(true);&nbsp; const remountFoo = () => {&nbsp; &nbsp; setShowFoo(false);&nbsp; &nbsp; Promise.resolve().then(() => setShowFoo(true));&nbsp; };&nbsp; return (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; <button onClick={() => dispatch(add())}>&nbsp; &nbsp; &nbsp; &nbsp; foo:{foo}&nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; <button onClick={() => setOther((o) => o + 1)}>&nbsp; &nbsp; &nbsp; &nbsp; other{other}&nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; <button onClick={remountFoo}>remount Foo</button>&nbsp; &nbsp; &nbsp; {showFoo && (&nbsp; &nbsp; &nbsp; &nbsp; <InitialFooContainer foo={foo} other={other} />&nbsp; &nbsp; &nbsp; )}&nbsp; &nbsp; </div>&nbsp; );};ReactDOM.render(&nbsp; <Provider store={store}>&nbsp; &nbsp; <App />&nbsp; </Provider>,&nbsp; 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>

富国沪深

我用来完成此功能的解决方案是复制foo,例如商店中的东西initialFoo,然后在需要的组件中挑选它。
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答