Reputation: 2839
I have a Chat
component that displays like a popup. I want to give users the possibility to open/close it as actions of deeper components. I have spent hours on it because I'll use the same pattern for other components. I would really appreciate a clean / React solution!
I have managed to get something working so far, but it really looks clumsy.
EDIT: I made a singleton of it to make it look better. Still wondering what the React way is.
import React, { Component } from 'react';
var instance = null;
class Chat extends Component {
constructor(props) {
super(props);
this.state = {
show: false
};
}
render() {
return this.state.show ? (<div>CHAT WINDOW</div>) : null;
}
open = () => this.setState({show: true})
close = () => this.setState({show: false})
}
export const openChat = () => instance.open();
export default function ChatSingleton(props) {
if (instance === null)
instance = new Chat(props);
return instance;
}
Now I can call open
/close
methods globally, like this:
import {openChat} from 'components/Chat';
export default function Foo(props) {
return (<div onClick={openChat}>Open Chat</div>);
}
I found solutions to similar problems on SO that use Redux or React Context, but I really want a simple solution. HOC would be ok.
Thanks!
Upvotes: 0
Views: 192
Reputation: 1113
Before looking for a solution try to understand if the "show" status must belong to chat or to his father(Chain - of - responsibility pattern) Once you understand this, here are some possible solutions
Using props:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
showChat: false
};
}
openChat = () => this.setState({showChat: true})
closeChat = () => this.setState({showChat: false})
render() {
return (
<div>
<Chat show={this.state.showChat}>
<Foo onOpenClick={this.openChat} onCloseClick={this.closeChat}>
</div>
}
}
Using Context:
class MyProvider extends React.Component {
state = {
isOpen: false
};
openChat = e => {
this.setState({ isOpen: true });
};
closeChat = e => {
this.setState({ isOpen: false });
};
render() {
return (
<MyProvider
value={{
state: this.state,
openChat: this.openChat,
openChat: this.closeChat
}}
>
<div >{this.props.children}</div
</MyProvider>
);
}
App:
import { MyProvider } from './ChatContext';
<MyProvider>
<Chat />
<OtherComponent>
<Foo>
</OtherComponent>
</MyProvider>
Foo:
import React, { Component } from 'react';
import { MyConsumer } from './ChatContext';
class Foo extends Component {
state = {};
render() {
return (
<MyConsumer>
{({ openChat }) => (
<div>
This is Foo
<button onClick={openChat}>open</span>
</div>
)}
</MyConsumer>
);
}
}
export default Foo;
Read more about context: https://kentcdodds.com/blog/how-to-use-react-context-effectively
or using React-redux
: https://react-redux.js.org/
Upvotes: 1