如何使用 redux 状态更新 React HOC 组件?
How to update react HOC component with redux state?
下面是 HOC,它也连接到 redux store。 WrappedComponent 函数不会在存储数据更改时获取 redux 状态。这里可能有什么问题?
export function withCreateHOC<ChildProps>(
ChildComponent: ComponentType,
options: WithCreateButtonHOCOptions = {
title: 'Create',
},
) {
function WrappedComponent(props: any) {
const { createComponent, title } = options;
const [isOpen, setisOpen] = useState(false);
function onCreateClick() {
setisOpen(!isOpen);
Util.prevDefault(() => setisOpen(isOpen));
}
return (
<div>
<ChildComponent {...props} />
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={Util.prevDefault(onCreateClick)}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={setisOpen}
createComponent={createComponent}
/>
</div>
);
}
function mapStateToProps(state: any) {
console.log('HOC mapStateToProps isOpen', state.isOpen);
return {
isOpen: state.isOpen,
};
}
// Redux connected;
return connect(mapStateToProps, {})(WrappedComponent);
}
期望从 ReduxStore 使用 isOpen 并在此处使用 WrappedComponent 更新相同的内容。有什么机会应该将其更改为 class 组件?
以上HOC用作:
export const Page = withCreateHOC(
PageItems,
{
createComponent: <SomeOtherComponent />,
title: 'Create',
},
);
概览
您不希望 isOpen
成为 WrappedComponent
的地方州。这个 HOC 的重点是从你的 redux store 访问 isOpen
。请注意,在此代码中的任何地方都没有更改 redux 状态的值。您想放弃本地状态,从 redux 访问 isOpen
,并 dispatch
在 redux 中更改 isOpen
的操作。
此外,我们必须用实际类型替换其中一些 any
!
我似乎有点怀疑您传递的是已解析的 JSX 元素,而不是作为 createComponent
的可调用组件(<SomeOtherComponent />
vs SomeOtherComponent
),但这是否正确或错误取决于您的 OpenDrawerWithClose
组件中的内容。我假设这里写的是正确的。
从技术上讲,使用 connect
没有任何问题,但在 HOC 内部使用 HOC 感觉有点奇怪,所以我将使用钩子 useSelector
和 useDispatch
.
循序渐进
我们想要创建一个函数,它接受一个组件 ComponentType<ChildProps>
和一些选项 WithCreateButtonHOCOptions
。您正在为 options.title
提供默认值,因此我们可以将其设为可选。 options.createComponent
是可选的还是必需的?
interface WithCreateButtonHOCOptions {
title: string;
createComponent: React.ReactNode;
}
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<ChildProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
我们 return 一个采用相同属性的函数,但没有 isOpen
或 toggleOpen
,如果这些是 ChildProps
的属性。
return function (props: Omit<ChildProps, 'isOpen' | 'toggleOpen'>) {
我们需要在解构步骤中为 options
设置默认值,以便只设置一个 属性。
const { createComponent, title = 'Create' } = options;
我们从 redux 状态访问 isOpen
。
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
我们创建了一个回调,将一个动作分派给 redux——您需要在您的 reducer 中处理它。我正在调度一个原始动作对象 {type: 'TOGGLE_OPEN'}
,但您可以为此创建一个动作创建器函数。
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({type: 'TOGGLE_OPEN'});
}
我们将把 isOpen
和 toggleOpen
这两个值作为 props 传递给 ChildComponent
以防它想使用它们。但更重要的是,我们可以将它们用作按钮和抽屉组件上的点击处理程序。 (注意:看起来抽屉想要一个带有 boolean
的道具 setIsOpen
,因此您可能需要稍微调整一下。如果抽屉仅在 isOpen
为 [=48 时显示=] 那么只需切换就可以了)。
代码
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<ChildProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
return function (props: Omit<ChildProps, 'isOpen' | 'toggleOpen'>) {
const { createComponent, title = 'Create' } = options;
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({ type: 'TOGGLE_OPEN' });
}
return (
<div>
<ChildComponent
{...props as ChildProps}
toggleOpen={toggleOpen}
isOpen={isOpen}
/>
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={toggleOpen}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={toggleOpen}
createComponent={createComponent}
/>
</div>
);
}
}
这个版本稍微好一点,因为它没有 as ChildProps
断言。我不想过多地关注“为什么”,但基本上我们需要坚持,如果 ChildProps
采用 isOpen
或 toggleOpen
道具,这些道具必须具有相同的类型正如我们提供的那样。
interface AddedProps {
isOpen: boolean;
toggleOpen: () => void;
}
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<Omit<ChildProps, keyof AddedProps> & AddedProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
return function (props: Omit<ChildProps, keyof AddedProps>) {
const { createComponent, title = 'Create' } = options;
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({ type: 'TOGGLE_OPEN' });
}
return (
<div>
<ChildComponent
{...props}
toggleOpen={toggleOpen}
isOpen={isOpen}
/>
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={toggleOpen}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={toggleOpen}
createComponent={createComponent}
/>
</div>
);
}
}
下面是 HOC,它也连接到 redux store。 WrappedComponent 函数不会在存储数据更改时获取 redux 状态。这里可能有什么问题?
export function withCreateHOC<ChildProps>(
ChildComponent: ComponentType,
options: WithCreateButtonHOCOptions = {
title: 'Create',
},
) {
function WrappedComponent(props: any) {
const { createComponent, title } = options;
const [isOpen, setisOpen] = useState(false);
function onCreateClick() {
setisOpen(!isOpen);
Util.prevDefault(() => setisOpen(isOpen));
}
return (
<div>
<ChildComponent {...props} />
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={Util.prevDefault(onCreateClick)}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={setisOpen}
createComponent={createComponent}
/>
</div>
);
}
function mapStateToProps(state: any) {
console.log('HOC mapStateToProps isOpen', state.isOpen);
return {
isOpen: state.isOpen,
};
}
// Redux connected;
return connect(mapStateToProps, {})(WrappedComponent);
}
期望从 ReduxStore 使用 isOpen 并在此处使用 WrappedComponent 更新相同的内容。有什么机会应该将其更改为 class 组件?
以上HOC用作:
export const Page = withCreateHOC(
PageItems,
{
createComponent: <SomeOtherComponent />,
title: 'Create',
},
);
概览
您不希望 isOpen
成为 WrappedComponent
的地方州。这个 HOC 的重点是从你的 redux store 访问 isOpen
。请注意,在此代码中的任何地方都没有更改 redux 状态的值。您想放弃本地状态,从 redux 访问 isOpen
,并 dispatch
在 redux 中更改 isOpen
的操作。
此外,我们必须用实际类型替换其中一些 any
!
我似乎有点怀疑您传递的是已解析的 JSX 元素,而不是作为 createComponent
的可调用组件(<SomeOtherComponent />
vs SomeOtherComponent
),但这是否正确或错误取决于您的 OpenDrawerWithClose
组件中的内容。我假设这里写的是正确的。
从技术上讲,使用 connect
没有任何问题,但在 HOC 内部使用 HOC 感觉有点奇怪,所以我将使用钩子 useSelector
和 useDispatch
.
循序渐进
我们想要创建一个函数,它接受一个组件 ComponentType<ChildProps>
和一些选项 WithCreateButtonHOCOptions
。您正在为 options.title
提供默认值,因此我们可以将其设为可选。 options.createComponent
是可选的还是必需的?
interface WithCreateButtonHOCOptions {
title: string;
createComponent: React.ReactNode;
}
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<ChildProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
我们 return 一个采用相同属性的函数,但没有 isOpen
或 toggleOpen
,如果这些是 ChildProps
的属性。
return function (props: Omit<ChildProps, 'isOpen' | 'toggleOpen'>) {
我们需要在解构步骤中为 options
设置默认值,以便只设置一个 属性。
const { createComponent, title = 'Create' } = options;
我们从 redux 状态访问 isOpen
。
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
我们创建了一个回调,将一个动作分派给 redux——您需要在您的 reducer 中处理它。我正在调度一个原始动作对象 {type: 'TOGGLE_OPEN'}
,但您可以为此创建一个动作创建器函数。
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({type: 'TOGGLE_OPEN'});
}
我们将把 isOpen
和 toggleOpen
这两个值作为 props 传递给 ChildComponent
以防它想使用它们。但更重要的是,我们可以将它们用作按钮和抽屉组件上的点击处理程序。 (注意:看起来抽屉想要一个带有 boolean
的道具 setIsOpen
,因此您可能需要稍微调整一下。如果抽屉仅在 isOpen
为 [=48 时显示=] 那么只需切换就可以了)。
代码
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<ChildProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
return function (props: Omit<ChildProps, 'isOpen' | 'toggleOpen'>) {
const { createComponent, title = 'Create' } = options;
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({ type: 'TOGGLE_OPEN' });
}
return (
<div>
<ChildComponent
{...props as ChildProps}
toggleOpen={toggleOpen}
isOpen={isOpen}
/>
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={toggleOpen}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={toggleOpen}
createComponent={createComponent}
/>
</div>
);
}
}
这个版本稍微好一点,因为它没有 as ChildProps
断言。我不想过多地关注“为什么”,但基本上我们需要坚持,如果 ChildProps
采用 isOpen
或 toggleOpen
道具,这些道具必须具有相同的类型正如我们提供的那样。
interface AddedProps {
isOpen: boolean;
toggleOpen: () => void;
}
function withCreateHOC<ChildProps>(
ChildComponent: ComponentType<Omit<ChildProps, keyof AddedProps> & AddedProps>,
options: Partial<WithCreateButtonHOCOptions>
) {
return function (props: Omit<ChildProps, keyof AddedProps>) {
const { createComponent, title = 'Create' } = options;
const isOpen = useSelector((state: { isOpen: boolean }) => state.isOpen);
const dispatch = useDispatch();
const toggleOpen = () => {
dispatch({ type: 'TOGGLE_OPEN' });
}
return (
<div>
<ChildComponent
{...props}
toggleOpen={toggleOpen}
isOpen={isOpen}
/>
<div>
<Component.Button
key={'add'}
big={true}
round={true}
primary={true}
onClick={toggleOpen}
className={'float-right'}
tooltip={title}
>
<Component.Icon material={'add'} />
</Component.Button>
</div>
<OpenDrawerWithClose
open={isOpen}
title={title}
setisOpen={toggleOpen}
createComponent={createComponent}
/>
</div>
);
}
}