Skip to content

Instantly share code, notes, and snippets.

@iamvery
Last active April 6, 2022 01:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamvery/f587c8ab3c9f02916e70e5a2bcda65dd to your computer and use it in GitHub Desktop.
Save iamvery/f587c8ab3c9f02916e70e5a2bcda65dd to your computer and use it in GitHub Desktop.

UNA Workshop

By: Jay Hayes https://iamvery.com https://twitter.com/iamvery

You're about to build a chat room. Cool!

  1. Make the Topics component display topics dynamically by injecting data as a property.

    -let Topics = () =>
    +let Topics = ({topics}) =>
       <div>
         <h4>Click on a topic...</h4>
         <ul>
    -      <li><a href="#">baseball</a></li>
    -      <li><a href="#">student loans</a></li>
    +      {topics.map(topic => <li><a href="#">{topic}</a></li>)}
         </ul>
       </div>
    
     // ...
    
     class App extends React.Component {
    +  constructor(props) {
    +    super(props)
    +    this.state = {topics: ['one', 'two']}
    +  }
    +
       render() {
         return (
           <div>
    -        <Topics />
    +        <Topics topics={this.state.topics} />
             <Messages />
             <hr />
             <Input />
           </div>
         )
       }
     }
  2. Fetch and display topics with a RESTful API request.

     class App extends React.Component {
       constructor(props) {
         super(props)
    -    this.state = {topics: ['one', 'two']}
    +    this.state = {topics: []}
       }
    
    +  componentWillMount() {
    +    fetch('https://dreadful-spider-42903.herokuapp.com/topics')
    +      .then(response => response.json())
    +      .then(json => this.setState({topics: json.data}))
    +  }
    +
       render() {
         return (
           <div>
             <Topics topics={this.state.topics} />
             <Messages />
             <hr />
             <Input />
           </div>
         )
       }
     }
  3. Make the Messages component display messages dynamically by injecting data as a property.

    -let Messages = () =>
    +let Messages = ({messages}) =>
       <div>
         <h4>Here's what people are saying...</h4>
         <ul className="list-group">
    -      <li className="list-group-item">Baseball is great!</li>
    -      <li className="list-group-item">Debt sux.</li>
    +      {messages.map(message => <li className="list-group-item">{message}</li>)}
         </ul>
       </div>
    
     // ...
    
     class App extends React.Component {
       constructor(props) {
         super(props)
    -    this.state = {topics: []}
    +    this.state = {messages: ['first', 'second'], topics: []}
       }
    
       render() {
         return (
           <div>
             <Topics topics={this.state.topics} />
    -        <Messages />
    +        <Messages messages={this.state.messages} />
             <hr />
             <Input />
           </div>
         )
       }
     }
  4. Click on topic to set chat topic in app.

    -let Topics = ({topics}) =>
    +let Topics = ({topics, setTopic}) =>
       <div>
         <h4>Click on a topic...</h4>
         <ul>
    -      {topics.map(topic => <li><a href="#">{topic}</a></li>)}
    +      {topics.map(topic => <li><a href="#" onClick={() => setTopic(topic)}>{topic}</a></li>)}
         </ul>
       </div>
    
     // ...
    
     class App extends React.Component {
       constructor(props) {
         super(props)
    -    this.state = {messages: ['first', 'second'], topics: []}
    +    this.state = {messages: [], topics: []}
       }
      
       componentWillMount() {
         fetch('https://dreadful-spider-42903.herokuapp.com/topics')
           .then(resp => resp.json())
           .then(json => this.setState({topics: json.data}))
       }
    
    +  setTopic(topic) {
    +    this.receiveMessage(`Joining ${topic}...`)
    +  }
    +
    +  receiveMessage(message) {
    +    this.setState(
    +      ({messages}) => ({messages: messages.concat(message)})
    +    )
    +  }
    +
       render() {
         return (
           <div>
    -        <Topics topics={this.state.topics} />
    +        <Topics topics={this.state.topics} setTopic={(topic) => this.setTopic(topic)} />
             <Messages messages={this.state.messages} />
             <hr />
             <Input />
           </div>
         )
       }
     }
  5. Actually connect to chat topic.

     class App extends React.Component {
       constructor(props) {
         super(props)
         this.state = {messages: [], topics: []}
    +    this.socket = new Phoenix.Socket('wss://dreadful-spider-42903.herokuapp.com/socket')
    +    this.socket.connect()
       }
      
       componentWillMount() {
         fetch('https://dreadful-spider-42903.herokuapp.com/topics')
           .then(resp => resp.json())
           .then(json => this.setState({topics: json.data}))
       }
    
       setTopic(topic) {
    +    if (this.channel) {
    +      this.channel.leave()
    +    }
    +
    +    this.channel = this.socket.channel(`chat:${topic}`)
    +    this.channel.join()
    +    this.channel.on('shout', ({data}) => this.receiveMessage(data))
         this.receiveMessage(`Joining ${topic}...`)
       }
    
       receiveMessage(message) {
         this.setState(
           ({messages}) => ({messages: messages.concat(message)})
         )
       }
    
       render() {
         return (
           <div>
             <Topics topics={this.state.topics} setTopic={(topic) => this.setTopic(topic)} />
             <Messages messages={this.state.messages} />
             <hr />
             <Input />
           </div>
         )
       }
     }

  6. Submit message to chat on topic.

    -let Input = () =>
    -  <form>
    +let Input = ({sendMessage}) =>
    +  <form onSubmit={(event) => {
    +    let form = event.target
    +    sendMessage(form.elements.message.value)
    +    form.reset()
    +    event.preventDefault()
    +  }}>
         <input name="message" className="form-control" placeholder="Say something interesting (and not gross)..." />
       </form>
    
     // ...
    
     class App extends React.Component {
       constructor(props) {
         super(props)
         this.state = {messages: [], topics: []}
         this.socket = new Phoenix.Socket('wss://dreadful-spider-42903.herokuapp.com/socket')
         this.socket.connect()
       }
    
       componentWillMount() {
         fetch('https://dreadful-spider-42903.herokuapp.com/topics')
           .then(resp => resp.json())
           .then(json => this.setState({topics: json.data}))
       }
    
       setTopic(topic) {
         if (this.channel) {
           this.channel.leave()
         }
    
         this.channel = this.socket.channel(`chat:${topic}`)
         this.channel.join()
         this.channel.on('shout', ({data}) => this.receiveMessage(data))
         this.receiveMessage(`Joining ${topic}...`)
       }
    
       receiveMessage(message) {
         this.setState(
           ({messages}) => ({messages: messages.concat(message)})
         )
       }
    
    +  sendMessage(message) {
    +    this.channel.push('shout', {data: message})
    +  }
    +
       render() {
         return (
           <div>
             <Topics topics={this.state.topics} setTopic={(topic) => this.setTopic(topic)} />
             <Messages messages={this.state.messages} />
             <hr />
    -        <Input />
    +        <Input sendMessage={(message) => this.sendMessage(message)} />
           </div>
         )
       }
     }

THAT'S ALL! Great job 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment