我们的项目有一个动态选项卡栏,使用redux和自定义挂钩来管理添加、删除和更改选择。我们为所有路由器和操作提供了自定义挂钩,以添加一个新的选项卡并显示与之相关的组件。这个选项卡栏在开发中可以很好地处理延迟加载,但在生产中总是会出现"TypeError:无法解析#Object的只读属性_status"(node srpts/build.js或react scripts build(,即使只使用React.lazy(() => import)
。以下是代码和组件堆栈:
TabHooks:
type AddType = (tabName: string, keepComponent: JSX.Element) => void;
export const useNewAliveTab = (): AddType => {
const dispatch = useDispatch();
const aliveRef = useRef<KeepAlive>();
return (tabName: string, keepComponent: JSX.Element) => {
const now = Date.now().toString();
const keepAliveElement = (
<Suspense fallback={<Loader type="converging-spinner" size="large" />}>
<KeepAlive aliveRef={aliveRef} name={now} key={now}>
<ErrorBoundary>{ keepComponent }</ErrorBoundary>
</KeepAlive>
</Suspense>
);
dispatch(
addNewTab({
tabName: tabName,
uuid: now,
element: keepAliveElement,
})
);
};
};
type DropType = (tabId: string) => void;
export const useDropAliveTab = (): DropType => {
const dispatch = useDispatch();
const { dropScope } = useAliveController();
return (tabId: string) => {
dispatch(removeTab(tabId));
dropScope(tabId);
};
};
type DropCurrentType = () => void;
export const useDropCurrentTab = (): DropCurrentType => {
const dispatch = useDispatch();
const { dropScope } = useAliveController();
const { current } = useSelector((state: RootState) => state.aliveTabs);
return () => {
dispatch(removeTab(current));
dropScope(current);
};
};
选项卡组件:
const AliveTabBarComponent = (): JSX.Element => {
const { tabAmount, tabs, current } = useSelector(
(state: RootState) => state.aliveTabs
);
const dispatch = useDispatch();
const dropTab = useDropAliveTab();
const onTabChange = (event: TabStripSelectEventArguments, newValue: string) =>
dispatch(changeSelectedTab(newValue));
return (
<>
<TabStrip selected={tabs.findIndex(item => item.id === current)} onSelect={e => onTabChange(e, tabs[e.selected].id)}>
{tabs.map((tab) => (
<TabStripTab
key={tab.id}
title={
tabAmount !== 0 && (
<GridLayout
gap={{ rows: 6, cols: 6 }}
rows={[{ height: "100%" }]}
cols={[{ width: "90%" }, { width: "10%" }]}>
<GridLayoutItem col={1} row={1}>
<Tooltip anchorElement="target" position="top">
<Typography.p textAlign="center">
{tab.tabName}
</Typography.p>
</Tooltip>
</GridLayoutItem>
<GridLayoutItem col={2} row={1}>
<Tooltip anchorElement="target" position="top">
<Button
iconClass="k-icon k-i-close"
onClick={(e) => {
e.stopPropagation();
dropTab(tab.id);
}}></Button>
</Tooltip>
</GridLayoutItem>
</GridLayout>
)
}>
{tab.keepElement}
</TabStripTab>
))}
</TabStrip>
</>
);
};
export default AliveTabBarComponent;
选项卡ReduxInitState:
interface AliveTabs {
tabs: AliveTabContentList;
current: string;
tabAmount: number;
}
interface AliveTabContent {
tabName: string;
id: string;
keepElement: JSX.Element;
}
type AliveTabContentList = Array<AliveTabContent>;
export const initialAliveTabsState: AliveTabs = {
tabs: new Array<AliveTabContent>(),
current: "",
tabAmount: 0,
};
TabReduxReducers
interface PayloadProps {
uuid: string;
tabName: string;
element: JSX.Element;
}
export const aliveTabsSlice = createSlice({
name: "aliveTabsSlice",
initialState: initialAliveTabsState,
reducers: {
changeSelectedTab(state, action: PayloadAction<string>) {
state.current = action.payload;
},
addNewTab(state, action: PayloadAction<PayloadProps>) {
state.tabAmount++;
state.current = action.payload.uuid;
state.tabs.push({
tabName: action.payload.tabName,
id: action.payload.uuid,
keepElement: action.payload.element,
});
},
removeTab(state, action: PayloadAction<string>) {
const index = state.tabs.findIndex(
(item) => item.id === action.payload
);
const isCurrentTab = state.current === action.payload;
if (index !== -1) {
state.tabAmount--;
state.tabs.splice(index, 1);
if (index === 0) {
if (state.tabAmount > 0) {
if (isCurrentTab) {
state.current = state.tabs[index].id;
}
} else {
state.current = "0";
}
} else if (index > state.tabAmount) {
if (isCurrentTab) {
state.current = state.tabs[state.tabAmount].id;
}
} else {
if (isCurrentTab) {
state.current = state.tabs[index - 1].id;
}
}
}
},
},
});
export default aliveTabsSlice.reducer;
我们像这样使用上面的:
const Layout = (): JSX.Element => {
const newTab = useNewAliveTab();
const LazyComponent = React.lazy(() => import("./TestComponent"));
return (
<>
<Button onClick={e => newTab("Test Tab", <LazyComponent />)}>Click Me</Button>
<AliveTabBarComponent />
</>
)
}
我们在开发中很好地运行了上面的代码,但在生产中总是得到TypeError,组件堆栈如下:
"
at Lazy
at i (http://localhost:3000/static/js/main.cb249e87.js:2:241027)
at t (http://localhost:3000/static/js/main.cb249e87.js:2:46905)
at t (http://localhost:3000/static/js/main.cb249e87.js:2:46905)
at Suspense
at ke (http://localhost:3000/static/js/main.cb249e87.js:2:50149)
at t (http://localhost:3000/static/js/main.cb249e87.js:2:50398)
at Oe (http://localhost:3000/static/js/main.cb249e87.js:2:51153)
at div
at div
at t (http://localhost:3000/static/js/main.cb249e87.js:2:45629)
at Suspense
at je (http://localhost:3000/static/js/main.cb249e87.js:2:53198)
at t (http://localhost:3000/static/js/main.cb249e87.js:2:53483)
at div
at t (http://localhost:3000/static/js/main.cb249e87.js:2:44132)
at J (http://localhost:3000/static/js/main.cb249e87.js:2:44736)
at t (http://localhost:3000/static/js/main.cb249e87.js:2:56400)"
不知道如何解决。我们使用这个选项卡来保持使用react激活的组件的活力,我已经尝试过这不是它的问题。也不是UI框架的问题,因为我们在Material UI V4和Kendo react上有同样的问题。
我遇到了同样的错误和类似的情况。
我将一个带有惰性加载元素的jsx对象传递到一个变量中,该变量在对话框组件中使用,与我设置变量的组件分离
setDialogBody(<><Suspense><LazyLoadedComponent/></Suspense></>);
它还在生产中的"#Object的只读属性_status"周围引发了一个错误。
我的解决方案是制作一个包含LazyLoadComComponent和LazyLoadComponent导入逻辑的新组件——我称之为包装器。
import React, {Suspense} from "react";
const LazyLoadComponent = React.lazy(() => import('./LazyLoadComponent'));
export default function LazyLoadComponentWrapper () {
return <Suspense><LazyLoadComponent/></Suspense>
}
然后我将包装器组件传递到相同的模式中:
setDialogBody(<LazyLoadedComponent/>);
我认为这简化了最小化代码,或者将懒惰的逻辑转移到了懒惰负载实际发生的地方,从而解决了一些复杂问题。不管怎样,它对我有效。
如果你尝试同样的方法并在这里使用包装器组件,也许它也会对你有用:
<Button onClick={e => newTab("Test Tab", <LazyComponentWrapper />)}>Click Me</Button>