问题
I created a reusable date picker component that I want to use throughout my React app. The issue that I'm running into is that the handler functions that get called during user interactions are identical in every React component where I use my date picker. This means I need to move them to an external file.
The question is once I move thess functions to an external file which is NOT a React component, how will I access the store and dispatch function?
Did some research and came across the idea of using middleware but as far as I know middleware is supposed to go in between action creators and reducers. In this particular case, I'm simply moving my handler functions to an external file, not really inserting anything in between action creators and reducers.
Say, my external file looks like this:
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// Import actions
import * as myActions from '../actions/myActions';
export const handleChangeDecadeStart = (datePickerId, value) => {
// Get current decade start
const currentDecadeStart = this.props.decadeStart;
// Set new decade start value
if(value === "prev") {
return this.props.actions.setDecadeStart(datePickerId, currentDecadeStart - 10);
} else {
return this.props.actions.setDecadeStart(datePickerId, currentDecadeStart + 10);
}
}
export const setDate = (datePickerId, value) => {
return this.props.actions.setDate(datePickerId, value);
}
function mapStateToProps(state) {
return {
decadeStart: state.calendars.decadeStart,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(myActions, dispatch)
};
}
connect(mapStateToProps, mapDispatchToProps)(???);
I tried to kind of modify what things look like in a React component and I'm not really sure if this makes sense. I'm particularly confused about the connect line at the bottom.
回答1:
There are two ways you might handle this. The first is currying, which is wrapping your event handlers into a function that takes this.props and allows the inner event handler to use them. For example in your handleChangeDecadeStart function you could do something like this:
const handleChangeDecadeStart = props => (datePickerId, value) => {
// Get current decade start
const currentDecadeStart = this.props.decadeStart;
// Set new decade start value
if(value === "prev") {
return this.props.actions.setDecadeStart(datePickerId,
currentDecadeStart - 10);
} else {
return this.props.actions.setDecadeStart(datePickerId,
currentDecadeStart + 10);
}
}
Then in your component's render function you would to this:
return (
<YourComponent onChangeDecadeStart={handleChangeDecadeStart(this.props)}/>
)
This way the event handler will have access to this.props without actually being inside the class.
The connect line at the end connects the store to your class, so it should receive your React component class as the final argument like this:
connect(mapStateToProps, mapDispatchToProps)(MyReactComponent)
The second, and in my opinion better way is to use a Higher-Order-Component. An HOC is just like the connect function, a function that takes a component and returns a component wrapping it adding some functionality. An example implementation:
const wrapWithHandlers = Component => class Wrapper extends Component {
handleChangeDecadeStart = (datePickerId, value) => {
// Get current decade start
const currentDecadeStart = this.props.decadeStart;
// Set new decade start value
if(value === "prev") {
return this.props.actions.setDecadeStart(datePickerId, currentDecadeStart - 10);
} else {
return this.props.actions.setDecadeStart(datePickerId, currentDecadeStart + 10);
}
}
render() {
return <Component onChangeDecadeStart={this.handleChangeDecadeStart} {...this.props} />
}
}
Then, when exporting your class you would do something like this:
export default wrapWithHandlers(MyComponent)
The function will wrap your component with a Higher-Order Component that provides it with the handlers needed, and you can re-use it with other Components. In your case, though, you also use connect, so your code will be:
export default connect(mapStateToProps, mapDispatchToProps)(wrapWithHandlers(MyComponent))
This pattern of a function calling a function calling a function etc.. is called function composition, which can be better expressed using the compose function in redux, in which case your code would be:
import { compose } from 'redux'
...
const enhancer = compose(connect(mapStateToProps, mapDispatchToProps), wrapWithHandlers);
export default enhancer(MyComponent);
Instead of doing this yourself, though, there are a lot of libraries out there that provide you with this kind of HOC out-of-the-box, recompose being an excellent example. Check out their withHandlers function which provides you with the exact functionality you need.
来源:https://stackoverflow.com/questions/45599409/moving-handler-functions-outside-of-react-component