Reputation: 164
I have two connected components, they both need the same parent so that they can talk to each other. However I would like to be able to render them anywhere, 100% separate. I have a jsfiddle example to show the idea, it is a terrible solution to my problem since I'm creating a new Img component whenever I need to change the props passed in. But it shows the idea. I feel like I'm probably going about this wrong but maybe there is a way to pass props to the Img without just making a new instance. Having a non React class be the parent is not ideal for sure.
Fiddle explanation: make an Img component that takes in a prop telling it if it should render or not make a Switch component that will change the prop passed into Img component when clicked
They can be rendered anywhere separately and are controlled by a parent class. The forceUpdate is just to make the example work, I know that is not a good use of it.
The code:
const Img = (props) => {
return (
<div><img style={{ display: props.isShowing ? 'inline' : 'none', width: '100px' }} src="http://blog.nationalgeographic.org/wp-content/uploads/2010/04/Greatest-Nature-Photographs-of-All-Time-3.jpg" /></div>
);
};
const Switch = (props) => {
return (
<div style={{ width: '50px', height: '50px', background: 'black', color: 'white'}} onClick={() => props.toggleImg()}>
click me
</div>
);
};
class MasterComponent {
constructor(outerThis) {
this.outerThis = outerThis;
this.toggleState = true;
this.img = <Img isShowing={ true } />;
this.switch = <Switch toggleImg={ () => this.toggleImg() } />;
}
toggleImg() {
this.toggleState = !this.toggleState;
this.img = <Img isShowing={ this.toggleState } />;
this.outerThis.forceUpdate();
}
}
class Example extends React.Component {
constructor(props) {
super(props);
this.masterComponent = new MasterComponent(this);
}
render() {
return <div>
{this.masterComponent.img}
{this.masterComponent.switch}
</div>;
}
}
edit: So the question is basically this. I want the 'MasterComponent' to be some sort of parent that gives you two children that interact with each other in the realm of state/props but are rendered separately like in the Example's render. So imagine importing MasterComponent and then using it like I did in the Example component without knowing what is going on behind the scenes. That is the design pattern I hoped for, but it doesn't seem achievable with React alone maybe.
My version of the MasterComponent is bad because I'm replacing the Img component with a new instance of Img with different props when I really just want to update the props it had. Using forceUpdate over setState is bad too but I'm less concerned about that.
I think since MasterComponent isn't a react component with state that can cause a rerender and Img and Switch aren't inside a render function where they can organically receive that state, maybe my idea doesn't work.
Upvotes: 0
Views: 82
Reputation: 13623
So... I don't know that this is a good pattern... it's not super React-y, but I think it would achieve what you are looking for. I haven't tested it, but I'm thinking something like this:
function getImgSwitchPair() {
const state = {
isShowing: true
};
const toggleImg = () => {
state.isShowing = !state.isShowing;
};
const Img = () => {
return (
<div><img style={{ display: state.isShowing ? 'inline' : 'none', width: '100px' }} src="http://blog.nationalgeographic.org/wp-content/uploads/2010/04/Greatest-Nature-Photographs-of-All-Time-3.jpg" /></div>
);
};
const Switch = () => {
return (
<div style={{ width: '50px', height: '50px', background: 'black', color: 'white'}} onClick={toggleImg}>
click me
</div>
);
};
return {
Img,
Switch,
toggleImg
};
}
class Example extends React.Component {
constructor(props) {
super(props);
const {Img, Switch} = getImgSwitchPair();
this.Img = Img;
this.Switch = Switch;
}
render() {
return (
<div>
<Img/>
<Switch/>
</div>
);
}
}
getImgSwitchPair
generates and returns a coupled Img
and Switch
component, as well as the toggleImg
function if you want to call it manually. We use a state
object to mutate to change isShowing
state.
I think this would work. However, it would exist and update state completely outside of the React lifecycle, which I think would be problematic. So while this may work, I'm not positive it is a good pattern.
I'm hesitant to post this w/o testing and knowing it may be a problematic pattern, but I'm hoping perhaps it gets you down the path of what you're looking for...
Upvotes: 1
Reputation: 361
So i think it's pretty dang good, however to make it more "React tm" like:
const Img = (props) => {
return (
<div>
<img
style={{ display: props.isShowing ? 'inline' : 'none', width: '100px' }}
src="http://blog.nationalgeographic.org/wp-content/uploads/2010/04/Greatest-Nature-Photographs-of-All-Time-3.jpg"/>
</div>
);
};
const Switch = (props) => {
return (
<div
style={{ width: '50px', height: '50px', background: 'black', color: 'white'}}
onClick={props.toggleImg}>
click me
</div>
);
};
class MasterComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
showImg: true,
};
this.toggleImg = this.toggleImg.bind(this);
}
toggleImg() {
this.setState({showImg: !this.state.showImg});
}
render() {
return <div>
<Img isShowing={this.state.showImg} />
<Switch toggleImg={this.toggleImg} />
</div>;
}
}
class Example extends React.Component {
render() {
return <div>
<MasterComponent />
</div>;
}
}
ReactDOM.render(
<Example />,
document.getElementById('container')
);
like this is more composable and using react Components classes
also you maybe interested in something like:
{this.state.showImg && <Img isShowing={true} />}
Upvotes: 0