如何解决我如何在不变的React功能组件中拥有一个变量的副本?
我在功能组件中有一个变量const foo: Array<ObjectExt> = useSelector(fooSelector);
。我想要从第一次加载组件开始,直到foo
不变时,该变量的副本。
在使用类组件时,我可以简单地使用const fooCopy = foo.slice();
,但这在这里不起作用,因为组件每次都会重新加载并且fooCopy
会发生变化。
如何在功能组件中实现这一目标?
解决方法
仅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>
,
一种解决方案是将标志设置为本地组件状态。如果该标志为假,则复制该值。否则,不要。
,我用于实现此功能的解决方案是在商店中复制foo
(类似于initialFoo
),然后在所需的组件中进行选择。
我希望从第一次加载组件时就获得此变量的副本,而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>
另一种方法是使用带有忽略foo的自定义比较功能的React.memo创建纯组件:
const { Provider,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,composeEnhancers(
applyMiddleware(() => (next) => (action) =>
next(action)
)
)
);
const InitialFoo = React.memo(
function InitialFoo(props) {
return (
<div>
<h3>initialfoo</h3>
<pre>{JSON.stringify(props,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,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>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。