Communication between React components using events
Feb 21, 2015 - 4 minutesHere is an example of a clean way to communicate between React components without getting stuck passing @prop
callbacks all around. Inspired by looking at the new Flux React utilities.
We’re going to start off with a simple HAML file:
1%script{src: "https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/react-with-addons.js"}
2%div{data: { ui: 'alerts' }}
3%div{data: { ui: 'widgets' }}
4:javascript
5 React.renderComponent(Widgets(), document.querySelector('[data-ui="widgets"]'))
6 React.renderComponent(Alerts(), document.querySelector('[data-ui="alerts"]'))
Next comes our Widget
component.
1{div, button} = React.DOM
2
3Widgets = React.createClass
4 render: ->
5 div className: 'widget',
6 button className: 'btn btn-primary', onClick: (=> @_sendMsg('Testing')), 'Click Me'
7 _sendMsg: (msg) ->
8 $('[data-ui="alerts"]').trigger("message", ["Widget clicked."])
On line 1 we’re defining some easy helper methods to access the React.DOM
object - otherwise on every line we’d be writing something like React.DOM.div
or whichever element we were going to call.
Line 4 is our render method. Everytime state gets mutated or the component is loaded, this method is called.
On line 6 we’re creating an anonymous function but passing in the local scope using a fat arrow =>
so we can access our other functions in the class. We call it inside an anonymous function so we can pass an argument to it, in this case the message.
Line 7 is our function that fires the event. I’m using the _sendMsg
syntax to denote it is a private function. The first argument to the jQuery event emitter is the event name, followed by a list of arguments.
Now lets write our Alert
handler and go through it line by line.
1{div} = React.DOM
2Alerts = React.createClass
3 messageTimeout: 5000
4 getInitialState: ->
5 messages: []
6
7 componentDidMount: ->
8 $('[data-ui="alerts"]').on 'message', (event, msg) =>
9 msgs = @state.messages
10 msgs.push(msg)
11 @setState(messages: msgs)
12
13 componentDidUpdate: ->
14 @state.messages.map (msg, index) =>
15 setTimeout(( => @_removeMsg(index)), @messageTimeout)
16
17 render: ->
18 div {},
19 @state.messages.map (msg, index) =>
20 div className: 'alert alert-info',
21 msg
22
23 _removeMsg: (index) ->
24 msgs = @state.messages
25 msgs.splice(index, 1)
26 @setState(messages: msgs)
Line 1 we’re doing the same thing as before, creating a little helper method.
Line 3 is a class variable (we also could have used props
here but I went with the class variable instead).
Line 4 is a function that defines the initial state of the component once it is mounted on the page. Here we are saying that there is an empty messages
array.
Line 7 is a life cycle event of a React component, called componentDidMount
which is called after the component has been rendered into the DOM and mounted.
Here we are telling jQuery to bind to any events that are triggered on the [data-ui="alerts"]
object and process them. We take the current messages from @state.messages
, push
the newest message on to the end and then finally call @setState
to mutate the components state.
Now the next part on line 13 is how we can gracefully remove messages after they have been rendered. componentDidUpdate
is another React life cycle event and is called after a render occurs (and renders occur because the component was updated).
We iterate over each message using the map
function and call setTimeout
with an anonymous function that calls @_removeMsg
and passes in an index. @messageTimeout
is how we access the class variable defined at the top of the file.
Line 17 is a render
call to display all the messages. Note that it is wrapped in a div because you can’t return a collection of objects from render, it must a single root element with nodes underneath.
Line 23 is our message removal function. We set @state.messages
to a local variable, remove one element at index
and then mutate the state by setting it to our local variable with @setState
.
Below is an example of the final product.
I’d like to thank my friend/co-worker Robert Pearce for getting me into React and showing me that everything doesn’t need to be jQuery!