Below is the HOC and it is connected to redux store too. The WrappedComponent function is not fetching the redux state on change of storedata. What could be wrong here?
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);
}
Expecting isOpen to be used from ReduxStore and update the same with WrappedComponent here. By any chance this should be changed to class component?
The above HOC is used as:
export const Page = withCreateHOC(
PageItems,
{
createComponent: <SomeOtherComponent />,
title: 'Create',
},
);
Overview
You don’t want
isOpen
to be a local state inWrappedComponent
. The whole point of this HOC is to accessisOpen
from your redux store. Note that nowhere in this code are you changing the value of your redux state. You want to ditch the local state, accessisOpen
from redux, anddispatch
an action to changeisOpen
in redux.Additionally we’ve got to replace some of those
any
s with actual types!It seems a little suspect to me that you are passing a resolved JSX element rather than a callable component as
createComponent
(<SomeOtherComponent />
vsSomeOtherComponent
), but whether that is correct or a mistake depends on what’s in yourOpenDrawerWithClose
component. I’m going to assume it’s correct as written here.There’s nothing technically wrong with using
connect
, but it feels kinda weird to use an HOC inside of an HOC so I am going to use the hooksuseSelector
anduseDispatch
instead.Step By Step
We want to create a function that takes a component
ComponentType<ChildProps>
and some optionsWithCreateButtonHOCOptions
. You are providing a default value foroptions.title
so we can make it optional. Isoptions.createComponent
optional or required?We return a function that takes the same props, but without
isOpen
ortoggleOpen
, if those were properties ofChildProps
.We need to set defaults for the
options
in the destructuring step in order to set only one property.We access
isOpen
from the redux state.We create a callback that dispatches an action to redux — you will need to handle this in your reducer. I am dispatching a raw action object
{type: 'TOGGLE_OPEN'}
, but you could make an action creator function for this.We will pass these two values
isOpen
andtoggleOpen
as props toChildComponent
just in case it want to use them. But more importantly, we can use them as click handlers on your button and drawer components. (Note: it looks like drawer wants a propsetIsOpen
that takes aboolean
, so you may need to tweak this a bit. If the drawer is only shown whenisOpen
istrue
then just toggling should be fine).Code
This version is slightly better because it does not have the
as ChildProps
assertion. I don’t want to get too sidetracked into the "why" but basically we need to insist that ifChildProps
takes anisOpen
ortoggleOpen
prop, that those props must have the same types as the ones that we are providing.Playground Link