Best practices for handling props connected to redux vs not connected to redux #327
Description
First of all; I wish there was someone who could write a blog entry/tutorial somewhere on the typescript redux matter. Especially now that redux itself has included typescript definitions, and since there has been a lot of discussing in the redux project about the best practices for this. I see that this project uses and extension, redux-typed, and I am not sure if these are all connected in regards to being a "good practice" or not.
I will use this post to explain my approach, as well as my actual problem, so that someone maybe can guide me to a better way of doing this.
When I create a new component I usually create it small, minimal. As I try to make them pure and dumb one of the immediate questions is which properties it should take. I then have a test-layout (or anywhere) that I use to inspect it and manually test it during development. A part of this process is deciding which props are required and not, trying to make sure that the component is well spec'ed.
In a typescripted environment, like for the projects created by this tool, this means setting the component up like this:
import * as React from 'react';
import { provide } from 'redux-typed';
import { ApplicationState } from '../store'; // The app store
import { actionCreators } from './store'; // This component's own store
interface MyComponentProps extends React.Props<any>{
propBool: boolean;
//...
}
export default class MyComponent extends React.Component<MyComponentProps, void> {
//...
}
BTW: I am bundling files with functionality for the same component in component-folders, like this:
Components
|- MyComponent
| |- index.tsx (holds the react component for MyComponent)
| |- store.ts (holds the state, actioncreators and reducer for MyComponent)
| |- styles.css (I am using CSS Modules, and this holds the styles for it, not shown in the code above)
| OtherComponent
...
- boot-client.tsx
- boot-server.tsx
- configureStore.ts
- routes.tsx
- store.ts
...
This makes it easier to reuse components. Adding on to a project is drag-dropping it in and wiring state and reducers in the app store.ts. Literally 2 lines of code. This has been great!
Ok, next step si to set up more properties. When adding methods I usually add them as
propMethod: Function;
... which obviously has no additional type-cehcking. Credited due to the fact that I am a typescript newbie... But, it works for my testing purposes.
Later I usually connect it to the redux store (if needed - of course). Doing that means that after the component class we add some extra code:
const provider = provide(
(state: ApplicationState) => state.myState,
actionCreators
);
export default provider.connect(MyComponent);
At this time I have to use the provider.allProps as a parameter for the component props, and move the export default
- otherwise it doesn't compile. Which also means that my test-properties are now no longer needed and can be deleted. That is fine for the scenarios where all the props are from the store. For other scenarios I found that I needed to use the withExternalProps
approach. Also, I found that using allProps alone missed out on some of the props provided by React.Props<any>
, like i.e. children
.
So, even without extra props beyond the store and actioncreators, I usually do this:
const provider = provide(
(state: ApplicationState) => state.myState,
actionCreators
).withExternalProps<React.Props<any>>();
Which seems to do the trick.
Ok, and then to the meat of this:
I am finding it "messy" with property-definitions, compared to javascript React.PropTypes, getting this to work. I do appreciate the type-checking, but to what cost. I have been training people on the team on this and they get a bit confused when we get into this area.
Also, after having wrapped the component, I am finding that there is no way to control properties from the outside. Which kinda makes sense, but it is sometimes nice to be able to override one of the store props. Also, I haven't yet included reselect
into the mix, and I am not sure that I dare doing it.
So, am I doing things the wrong way? Are there better ways? Or, are there any docs/tutorials or similar out there that could help me understand this better (if my understanding of this seems to be the issue)? I know I need more typescript training, btw, and working on it :).