I am having trouble figuring out what the best strategy is to preserve my original component's function (specifically setSelected() when wrapping it into a higher order function with Redux's connect()
. I am making use of a great set of components for radio buttons for my frontend app:
Radio.js
:
class Radio extends React.Component {
constructor(props) {
super(props);
this.state = {selected: props.selected};
}
toggle() {
console.log(this.context)
const {onChange} = this.context.radioGroup;
const selected = !this.state.selected;
this.setState({selected});
// this.props.changeRadioGroupState(this.props.option);
onChange(selected, this);
}
setSelected(selected) {
this.setState({selected});
}
render() {
const activeStyle = {
opacity: 0.1
}
const grayStyle = {
opacity: 0.5
}
let classname = 'form__big_button'
return (
<button type="button"
className={classname}
onClick={this.toggle.bind(this)}
style={this.state.selected ? activeStyle : grayStyle }>
{this.props.option}
</button>
);
}
}
Radio.contextTypes = {
radioGroup: React.PropTypes.object
};
It has a parent component called RadioGroup.js
:
import React from 'react';
class RadioGroup extends React.Component {
constructor(props) {
super(props);
this.options = [];
this.changeRadioGroupState = this.changeRadioGroupState.bind(this);
}
getChildContext() {
const {name} = this.props;
return {radioGroup: {
name,
onChange: this.onChange.bind(this)
}};
}
changeRadioGroupState(selectedOption){
this.setState({selectedOption: selectedOption});
}
onChange(selected, child) {
console.log(this.options)
this.options.forEach(option => {
if (option !== child) {
option.setSelected(!selected);
}
});
}
render() {
let children = React.Children.map(this.props.children, child => {
let newElement = React.cloneElement(child, {
ref: (component => {this.options.push(component);})
});
return newElement;
});
return <div className="radio-group">{children}</div>;
}
}
RadioGroup.childContextTypes = {
radioGroup: React.PropTypes.object
};
export default RadioGroup;
Together, it's rendered very simply:
<RadioGroup>
<Radio id="1" selected={true} option="Opt1"/>
<Radio id="2" selected={false} option="Opt2"/>
</RadioGroup>
However, I want to wire up Radio
to my Redux store so I can update the selected radio button:
const mapStateToProps = (state) => {
return {searchType: state.searchType}
}
export default connect(mapStateToProps)(Radio);
This results, however, in an error on the browser:
Uncaught TypeError: option.setSelected is not a function
at eval (RadioGroup.js?6ccb:27)
at Array.forEach (<anonymous>)
at RadioGroup.onChange (RadioGroup.js?6ccb:25)
at Radio.toggle (Radio.js?9dd4:17)
at Object.ReactErrorUtils.invokeGuardedCallback (ReactErrorUtils.js?9efc:71)
at executeDispatch (EventPluginUtils.js?68fe:79)
at Object.executeDispatchesInOrder (EventPluginUtils.js?68fe:102)
at executeDispatchesAndRelease (EventPluginHub.js?daec:43)
at executeDispatchesAndReleaseTopLevel (EventPluginHub.js?daec:54)
at Array.forEach (<anonymous>)
This is clearly a result of the connect()
component wrapping my Radio
component- it no longer exposes a setSelected()
function. What is the best way to preserve my original component's functions when wrapping it in a higher order component with the Redux connect()
function?
I've tried this.setSelected = this.setSelected.bind(this)
binding in the constructor, but it looks like the connect()
function returns a completely different type of object- a Connect
object.