I've got a small react app I'm playing with, just go get the hang of the library. The app is just a series of lists, which are populated from a server. When a list item is clicked, the value of that item is added to a list filters at the app level, which will then be used to call new data to populate the lists.
The problem is that I can't seem to get my lists to reconcile with the new data from the app (parent), even when calling setState
. Here's my code (coffee):
###
@jsx React.DOM
###
{div, h1, h2, h4, ul, li, form, input, br, p, strong, span, a} = React.DOM
SearchApp = React.createClass
handleTopItemClick: (filter) ->
facet = filter.field
filters = @state.filters
if filters.facets[facet] and filters.facets[facet].length > 0
filters.facets[facet].push filter.value
else
filters.facets[facet] = [filter.value]
strArr = []
_.each filters.facets, (valArr, field) ->
_.each valArr, (val) ->
strArr.push "+(#{field}:\"#{val}\")"
@setState
filters: filters
queryStr: strArr.join(' ').trim()
getInitialState: ->
filters:
facets: {}
queryStr: ''
render: ->
(div {
id: 'content'
className: "search-wrap"
},
(h1 {}, "Search")
(div
id: 'widgets',
(TopList
title: 'Top Domains'
params:
query: @state.queryStr
field: 'domain'
onItemClick: @handleTopItemClick
)
(TopList
title: 'Top Senders'
params:
query: @state.queryStr
field: 'from'
onItemClick: @handleTopItemClick
)
(TopList
title: 'Top Recipient'
params:
query: @state.queryStr
field: 'recipient'
onItemClick: @handleTopItemClick
)
)
)
TopItem = React.createClass
getDefaultProps: ->
value: ''
count: 0
field: null
render: ->
(li {},
(a {
onClick: @handleClick
className: 'top-item-filter'
title: @props.value
},
(strong {}, @props.value)
(span {}, @props.count)
)
)
handleClick: (event) ->
event.preventDefault()
@props.onItemClick @props.value
TopList = React.createClass
getInitialState: ->
data: []
params: @props.params
componentWillReceiveProps: (nextProps) ->
@setState params: nextProps.params
componentWillMount: ->
request.post("/facet").send(@state.params).end (results) =>
@setState data: JSON.parse(results.text)
render: ->
itemNodes = _.map @state.data, (item) =>
key = item.value
TopItem
value: item.value
count: item.count
key: key
onItemClick: @handleItemClick
(div {className: 'widget top-item'},
(h2 {className: 'widget-header'}, "#{@props.title}")
(ul {className: 'top-items-list'}, itemNodes)
)
handleItemClick: (value) ->
@props.onItemClick
value: value
field: @props.params.field
React.renderComponent SearchApp(null), document.getElementById("content")
The lists all render fine the first time around, fetching the unfiltered data as expected. When I click on a list item, the SearchApp
receives the event, and updates its own state accordingly. componentWillReceiveProps
is properly called on the TopList
classes, but the setState
call there doesn't update their state, and thus, they aren't reconciling. I've verified that nextProps
contains the new information. What am I missing?
There isn't really any reason to save the props in state; and it's much less error prone to keep props as the source of truth. It also simplifies the code a bit.
For the actual issue, though; componentWillMount is only called once here. If you want to repeat the AJAX request when new params are passed; you can do that like so:
TopList = React.createClass
getInitialState: ->
data: []
getSearchResultsFromServer: (params) ->
request.post("/facet").send(params).end (results) =>
if @isMounted()
@setState data: JSON.parse(results.text)
componentWillReceiveProps: (nextProps) -> @getSearchResultsFromServer nextProps.params
componentDidMount: -> @getSearchResultsFromServer @props.params
render: ->
itemNodes = _.map @state.data, (item) =>
key = item.value
TopItem
value: item.value
count: item.count
key: key
onItemClick: @handleItemClick
(div {className: 'widget top-item'},
(h2 {className: 'widget-header'}, "#{@props.title}")
(ul {className: 'top-items-list'}, itemNodes)
)
handleItemClick: (value) ->
@props.onItemClick
value: value
field: @props.params.field
Ideally, your ajax library would support aborting requests; and in componentWillUnmount you abort those requests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With