0

I come from vue.js, so I'm still a bit confused about react.

I have a Message component, that shows up flash messages:

import React, {Component} from "react";

export default class Message extends Component {

    constructor(props) {
        super(props);
        this.state = {messages: []};
    }

    setMessage(type, body) {
        this.setState({messages: this.state.messages.concat({type: type, body: body})})
        setTimeout(() => {
            this.removeMessage()
        }, 10000);
    }

    removeMessage() {
        let messages = this.state.messages;
        messages.shift();
        this.setState({messages: messages});
    }

    render() {
        return (
            <div className="uk-messages">
                {
                    this.state.messages.map((message, index) => {
                        if (message.type === 'error') {
                            message.type = 'danger';
                        }

                        return (
                            <div className={'uk-alert-' + message.type} data-uk-alert key={index}>
                                <p>{message.body}</p>
                            </div>
                        )
                    })
                }
            </div>
        )    
    }    
}

I use this component in my Index component:

import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter as Router, Link, Route, Redirect} from 'react-router-dom'
import Auth from './helpers/auth'
import Message from './helpers/message'

class Index extends Component {
    constructor(props) {
        super(props);
        this.state = {
            authState: Auth.state,
        };
        Auth.initialize();    
    }

    render() {
        return (    
            <Router>
                <div className="uk-flex uk-flex-column">
                    <nav className="uk-navbar-container" data-uk-navbar>
                        <div className="uk-navbar-left">
                            <a className="uk-navbar-item uk-logo" href="#"><img src={'images/logo.png'}/></a>

                            <ul className="uk-navbar-nav">
                                <li><Link to="/">Home</Link></li>
                            </ul>
                        </div>
                        <div className="uk-navbar-right">
                            <ul className="uk-navbar-nav">
                                <li>
                                    <a href="#" className="uk-open" aria-expanded="true">
                                        <span data-uk-icon="icon: user" />
                                    </a>

                                </li>

                            </ul>
                        </div>
                    </nav>
                    <div className="uk-flex-grow-1">
                        <Route path="/" component={Welcome} />
                    </div>
                    <footer>
                        &copy; 2018 stackoverflow.com
                    </footer>
                    <Message />
                </div>    
            </Router>
        );
    }

}

if (document.getElementById('root')) {
    ReactDOM.render(<Index/>, document.getElementById('root'));
}

Ok, so the Index component is the one I start with. I import the Message component and render it. That works. In chrome react console, I select the Message tag and can add some messages with

$r.setMessage('success','Greetings my old friend!')

The message appears. So now, how can I use the method setMessage in my Index component? In vue.js it's so simple (use $parent or $children)...

2
  • 2
    I see answers popping up mentioning refs, but before you get excited about them, I recommend you read reactjs.org/docs/refs-and-the-dom.html Commented Jul 19, 2018 at 9:42
  • Yeah you should avoid this bottom up dataflow in React and inverse it via passing messageArray to Message Component and handle this setMessage stuff in the Index Commented Jul 19, 2018 at 9:48

3 Answers 3

3

The most direct way to access the methods of another component is to pass refs, but you should probably approach the issue in a different way. Once you start tying your components together with refs, you make them less reusable and more difficult to work with.

If the list of messages were stored at a higher level, and passed into your Message component as a prop, then you would have no need for refs.

So I would recommend moving messages into the state of the Index component (or possibly even to a store), then passing messages as a prop to <Message messages={this.state.messages} />.

If you also want to manipulate the messages from within the Message component, then also pass whatever callbacks from <Index> that you need.

Below is a toy example showing the general idea of storing the state in the parent component and passing messages and addMessage as props to <Message>.

const Message = ({ messages, addMessage }) => (
  <div>
    {messages.map(({ text, id }) => <div key={id}>{text}</div>)}
    <button onClick={addMessage}>add</button>
  </div>
);

class Index extends React.Component {
  constructor(props) {
    super(props);
    this.addMessage = this.addMessage.bind(this);
  }

  state = {
    messages: [],
    id: 0
  };
  
  addMessage() {
    this.setState({ 
      messages: [...this.state.messages, { text: "new message", id: this.state.id }],
      id: this.state.id + 1
    });
  }
  
  render() {
    return <Message messages={this.state.messages} addMessage={this.addMessage} />
  }
}

ReactDOM.render(<Index/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Sign up to request clarification or add additional context in comments.

Comments

1

You can use refs to get the reference to the element e.g.

<Message refs={(ref) => this.messageElement = ref }/>

With that you can call methods on it from anywhere after it has been assigned

this.messageElement.setMessage();

Comments

1

You can use refs (references) as

 <Message ref={(c) => this.messageComponent = c} />

and make functions of Message component with context bound so that it is accessible in other components like

Message Component

 constructor(props) {
        super(props);
        this.state = {messages: []};
        this.setMessage = this.setMessage.bind(this);
    }

Usage in Index component

  this.messageComponent.setMessage('type','body');

Read more of refs here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.