T
T
tj572018-12-29 17:11:28
JavaScript
tj57, 2018-12-29 17:11:28

How to correctly render a component based on state in React?

I have a chat component:
5c277f3e85178697502586.jpeg

import React from "react";
import io from "socket.io-client";

class Chat extends React.Component{
    constructor(props){
        super(props);

        this.state = {
            username: '',
            message: '',
            messages: []
        };

        this.socket = io('localhost:5000');

        this.socket.on('RECEIVE_MESSAGE', function(data){
            addMessage(data);
        });

        const addMessage = data => {
            console.log(data);
            this.setState({messages: [...this.state.messages, data]});
            console.log(this.state.messages);
        };

        this.sendMessage = ev => {
            ev.preventDefault();
            this.socket.emit('SEND_MESSAGE', {
                author: this.state.username,
                message: this.state.message
            })
            this.setState({message: ''});

        }
    }
    render(){
        return (
            <div className="container">
                <div className="row">
                    <div className="col-4">
                        <div className="card">
                            <div className="card-body">
                                <div className="card-title">Global Chat</div>
                                <hr/>
                                <div className="messages">
                                    {this.state.messages.map(message => {
                                        return (
                                            <div>{message.author}: {message.message}</div>
                                        )
                                    })}
                                </div>

                            </div>
                            <div className="card-footer">
                                <input type="text" placeholder="Username" value={this.state.username} onChange={ev => this.setState({username: ev.target.value})} className="form-control"/>
                                <br/>
                                <input type="text" placeholder="Message" className="form-control" value={this.state.message} onChange={ev => this.setState({message: ev.target.value})}/>
                                <br/>
                                <button onClick={this.sendMessage} className="btn btn-primary form-control">Send</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

export default Chat;

I need to divide this component into 2 : the first one is the username input field and the "Join Chat" button, the second one is the chat itself, but without the username input field. First, the first component is rendered, and after entering a name and submitting it, the second one is rendered. How to implement it correctly? I did this, but it doesn't work. And how to implement the Join method, which fires when the button in the first component is clicked and adds the username to the state?
const ChatComponent = () => {
          return (
          <div className="container">
            <div className="row">
              <div className="col-4">
                <div className="card">
                  <div className="card-body">
                      <div className="card-title">Chat</div>
                      <hr/>
                      <div className="messages">
                        {this.state.messages.map(message => {
                          return (
                              <div>{message.author}: {message.message}</div>
                                )
                          })}
                      </div>
                  </div>
                  <div className="card-footer">
                      <input type="text" placeholder="Message" className="form-control"/>
                      <br/>
                      <button onClick={ this.sendMessage} className="btn btn-primary form-control">Send</button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          );
       }

      const JoinComponent = () => {
        return (
          <div className="card-footer">
            <input type="text" placeholder="Username" className="form-control"/>
            <br/>
            <button onClick={this.Join} className="btn btn-primary form-control">Join chat</button>
          </div>
        );
      }


render() {
      const username = this.state.username;
      return (
        <div>
        {this.state.username ? (
         <ChatComponent/>
          ) : (
         <JoinComponent />
      )};
      </div>
    );
  }

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Magrian, 2019-01-02
@tj57

Read about props and the documentation in general.
The solution to your problem in my vision:

import React from "react";
import io from "socket.io-client";
class ChatComponent extends React.Component {
  state = { message: "" }
  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="col-4">
            <div className="card">
              <div className="card-body">
                <div className="card-title">Chat</div>
                <hr />
                <div className="messages">
                  {this.props.messages.map(message => {
                    return (
                      <div>{message.author}: {message.message}</div>
                    )
                  })}
                </div>
              </div>
              <div className="card-footer">
                <input
                  type="text"
                  placeholder="Message"
                  className="form-control"
                  value={this.state.message}
                  onChange={ev => this.setState({ message: ev.target.value })} />
                <br />
                <button onClick={() => this.props.sendMessage(this.state.message)} className="btn btn-primary form-control">Send</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

class JoinComponent extends React.Component {
  state = { username: "" }
  render() {
    return (
      <div className="card-footer">
        <input
          type="text"
          placeholder="Username"
          className="form-control"
          value={this.state.username}
          onChange={ev => this.setState({ username: ev.target.value })} />
        <br />
        <button onClick={() => this.props.click(this.state.username)} className="btn btn-primary form-control">Join chat</button>
      </div>
    );
  }
}

class Chat extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      username: '',
      messages: []
    };

    this.socket = io('localhost:5000');

    this.socket.on('RECEIVE_MESSAGE', function (data) {
      addMessage(data);
    });

    const addMessage = data => {
      console.log(data);
      this.setState({ messages: [...this.state.messages, data] });
      console.log(this.state.messages);
    };
  }
  
  sendMessage = (message) => {
    this.socket.emit('SEND_MESSAGE', {
      author: this.state.username,
      message: message
    })
    this.setState({ message: '' });
  }
  
  joinToChat = (username) => {
    this.setState({ username })
  }

  render() {
    if (this.state.username) {
      return <ChatComponent 
            messages={this.state.messages} 
            sendMessage={this.sendMessage} />}
            
    else return <JoinComponent click={this.joinToChat} />
  }
}

export default Chat;

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question