How to "dynamically" `connect` to different states with React/Preact?
I see that connect can be used like this:
connect( [ 'var1', 'var2' ], actions )
Which works well... however I want to init a component, and pass the state name into it somehow...
The problem: I have a store, which based on user interaction (from the dom) will create a new state object eg state.field_${id}, and then dynamically render a Preact component... I want to pass down the state name / ID into a preact component/wrapper so it can connect to its own state object - field_${id},
The only thing I can think of, is to just connect it to the whole state, pass in the ID, and then filter that out manually...
const allState = ( state ) => {
return state;
};
export const MyComponent = connect(
allState,
actions
)( ( props ) => {
const myState = props[ `field_${ props.id }` ];
return <>...</>;
} );
But it doesn't sit right with me, and the component would naturally re-render on any state change rather the part we're after... .
Is this possible / are there any other approaches I might have missed?
Thanks!
Ok so I might have answered my own question.
I had a peek at the connect function and stole some ideas from there.
What I've done:
- Create a new context (the context is not exposed in unistore/preact) and ensure to pass the store as a prop to the provider
- Wrap this around the app just like
unistore/preact - Then use the
useContexthook to supply the store to the component - Update local state to keep track of the updates and trigger re-renders
- Wrap as a HOC
const StoreContext = createContext( {} );
// Make sure a StoreContext Provider is around the app
const getStateByKey = ( state, key ) => {
return state[ key ] ?? null;
};
const withStoreKey = ( WrappedComponent ) => ( { storeKey, ...rest } ) => {
const store = useContext( StoreContext );
const [ localState, setLocalState ] = useState(
getStateByKey( store.getState(), storeKey )
);
const receiveState = ( state ) => {
setLocalState( getStateByKey( state, storeKey ) );
};
useEffect( () => {
store.subscribe( receiveState );
return () => {
store.unsubscribe( receiveState );
};
}, [] );
if ( localState === null ) {
return null;
}
return ( <WrappedComponent { ...localState } { ...rest } /> );
};
So now I can do:
const MyComponent = () => {
return <div>Hello wrld</div>;
};
const MyConnectedComponent = withStoreKey( MyComponent );
const App = () => {
return <MyConnectedComponent storeKey={ 'customStoreKey' } more={ 'props' } />;
};
Using this method, I don't need to import unistore/preact and can designate specific props from a store's state to specific components...
Open to any suggestions of doing this another way but I think I'm quite happy with this implementation :)