React - Rails 5 Action Cable - reactjs

I've tried multiple things now but i cannot seem to get messages to be broadcasted to the clients.
In this application, i got a react component that does the channel subscription. My console output for application connected is showing up in my log but received is just not being called.
My React component:
# app/assets/javascripts/components/records.js.coffee
App.record = null
#Records = React.createClass
getInitialState: ->
#setupSubscription()
records: #props.data
getDefaultProps: ->
records: []
setupSubscription: ->
App.record = App.cable.subscriptions.create "RecordChannel",
connected: ->
console.log "connected to RecordChannel"
# Timeout here is needed to make sure Subscription
# is setup properly, before we do any actions.
#setTimeout: ->
# this.perform 'follow',
# {message_id: this.message_id}
# ,1000
received: (data) ->
console.log "data received"
#addRecord(data)
#record_subs = App.record
addRecord: (record) ->
records = React.addons.update(#state.records, { $push: [record] })
#setState records: records
deleteRecord: (record) ->
index = #state.records.indexOf record
records = React.addons.update(#state.records, { $splice: [[index,1]] })
#replaceState records: records
updateRecord: (record,data) ->
index = #state.records.indexOf record
records = React.addons.update(#state.records, { $splice: [[index,1,data]] })
#replaceState records: records
credits: ->
credits = #state.records.filter (val) -> val.amount >= 0
credits.reduce ((prev, curr) ->
prev + parseFloat(curr.amount)
), 0
debits: ->
debits = #state.records.filter (val) -> val.amount < 0
debits.reduce ((prev, curr) ->
prev + parseFloat(curr.amount)
), 0
balance: ->
#debits() + #credits()
render: ->
React.DOM.div
className: 'records'
React.DOM.h2
className: 'title'
'Records'
React.DOM.div
className: 'row'
React.createElement AmountBox, type: 'success', amount: #credits(), text: 'Credit'
React.createElement AmountBox, type: 'danger', amount: #debits(), text: 'Debit'
React.createElement AmountBox, type: 'info', amount: #balance(), text: 'Balance'
React.createElement RecordForm, handleNewRecord: #addRecord
React.DOM.hr null
React.DOM.table
className: 'table table-bordered'
React.DOM.thead null,
React.DOM.tr null,
React.DOM.th null, 'Date'
React.DOM.th null, 'Title'
React.DOM.th null, 'Amount'
React.DOM.th null, 'Actions'
React.DOM.tbody null,
for record in #state.records
React.createElement Record, key: record._id["$oid"], record: record, handleDeleteRecord: #deleteRecord, handleEditRecord: #updateRecord
Not sure which other piece of code you need to see, just let me know and i will post it

i finally figured out what was wrong in my application. My subscription method was wrong in the stack. Turns out that i was using stream_for instead of stream_for in my subscription!
class RecordChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
stream_for ("record_#{current_user['info']['nickname']}_channel")
end
to
class RecordChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
stream_from ("record_#{current_user['info']['nickname']}_channel")
end

Related

How to add copied dictionaries to array in Javascript?

I have function named "createdAtRefine" to change items' createdAt from milliseconds to MM/DD below.
const createdAtRefine = (datasum) => {
for (let i = 0; i < datasum.length; ++i) {
datasum[i].createdAt = `${
new Date(parseInt(datasum[i].createdAt)).getMonth() + 1
}/${new Date(parseInt(datasum[i].createdAt)).getDate()}`;
}
return datasum;
};
And I added several dictionary arrays to dataSumArray below.
datasumArray was set [] empty array.
datasumArray.push(
...feedData.seeAdminAllFeeds,
...poemData.seeAdminAllPoems,
...fpLikesData.seeAdminAllLikes,
...fpCommentsData.seeAdminAllComments,
...pedometerData.seeAdminAllPedometers
);
feedData.seeAdminAllFeeds looks like below.
-> [{__typename: 'Feed', id: '3', createdAt: '18382034'}, {__typename: 'Feed', id: '3', createdAt: '18382034'}]
Once all these dictionaries were added to one array I need to change createdAt from milliseconds to 'MM/DD' with above "createdAtRefind" function.
So i run below code.
let createdAtRefind = createdAtRefine(datasumArray);
Then outcome is good, it change to
-> [{__typename: 'Feed', id: '3', createdAt: '10/8'}, {__typename: 'Feed', id: '3', createdAt: '10/8'}]
But Problem is I need to call all this function at first with useEffect like below.
useEffect(() => {
if (
feedData !== undefined &&
feedData !== null &&
poemData !== undefined &&
poemData !== null &&
fpLikesData !== undefined &&
(fpLikesData !== null) & (fpCommentsData !== undefined) &&
fpCommentsData !== null &&
pedometerData !== undefined &&
pedometerData !== null
) {
datasumArray.push(
...feedData.seeAdminAllFeeds,
...poemData.seeAdminAllPoems,
...fpLikesData.seeAdminAllLikes,
...fpCommentsData.seeAdminAllComments,
...pedometerData.seeAdminAllPedometers
);
let createdAtRefind = createdAtRefine(datasumArray);
setDatasum(createdAtRefind);
}
}, [feedData, poemData, fpLikesData, fpCommentsData, pedometerData]);
When I first call this function, it works great.
But if I go to another screen and go back to this screen, **createdAtRefind** becomes all createdAt as '1/1'.
-> [{__typename: 'Feed', id: '3', createdAt: '1/1'}, {__typename: 'Feed', id: '3', createdAt: '1/1'}]
The reason is when I run "createdAtRefine" function, it change the initial data of feedData.seeAdminAllFeeds.
So initial data becomes
-> [{__typename: 'Feed', id: '3', createdAt: '10/8'}, {__typename: 'Feed', id: '3', createdAt: '10/8'}]
And so I run two times createdAtRefind function, so it becomes '1/1'..
I want to run this createdAtRefind function just once when this data comes regardless of screen change.
You're mutating your objects inside the dataSum array. It's best practice in JS, especially in React to avoid mutation. You're getting 1/1 because new Date(parseInt("10/8")).getMonth() + 1 gives you the number 1.
What you can do is use map
const createdAtRefine = (datasum) => {
return datasum.map((data) => ({
...data, // Spread data into new object
createdAt = `${new Date(parseInt(datasum[i].createdAt)).getMonth() + 1}/${new Date(parseInt(datasum[i].createdAt)).getDate()}`
}))
};
Here for each dataSum element I create a new objects with the same keys and values as the original object except for createdAt.
If you're planning on displaying this createdAt date another way to go would be to keep the original datasum array with createdAt in ms and convert only when displaying the date

React - deleting array elements stored in state deletes wrong element

I've been beating my head against this for long enough that I'm not sure I'd even see an obvious problem.
The App has rows, and each row has cells of varying sizes. Adding rows works. Adding cells works. Deleting rows however, does not. If I click the delete button for row 0, row 2 disappears. If I click the button for row 1, row 2 disappears. Looking at the react dev tools, it appears that under the hood the right row is being deleted (in state), the right row is selected for deletion, but no matter what it doesn't render the way I would expect (if I hit delete for row 0, row 0 disappears, for example).
If I delete from the highest row down it works, but if I start at the zeroth row it won't let itself be deleted.
deleteRowButtonHandler of the App class is where I've been focusing my efforts.
deleteRowButtonHandler( e )
{
const targetRow = parseInt( e.target.id )
console.log( "TARGETING: ", targetRow )
const currentRows = this.state.rows
let newArray = []
currentRows.forEach( entry =>
{
console.log( "Looking at ", entry.id )
if( entry.id == targetRow )
console.log( 'skipping entry with id', entry.id )
else
newArray.push( entry )
})
console.log( newArray )
this.setState( {rows: newArray})
//let newRows = currentRows.filter( thisRow => {console.log( thisRow.id + ' and the target ' + targetRow);
// if( thisRow.id == targetRow )
// {
// console.log( thisRow )
// console.log( '...is the row that we are trying to delete' )
// }
// return thisRow.id !== targetRow}
//)
//this.setState( {rows: newArray}, console.log( "NEW STATE:", this.state ))
}
I've tried a few variations (filter, splice, doing it in a few lines, doing it in a lot of lines, etc) so I'm not particularly proud of that chunk of code at the moment.
https://codepen.io/philipvdg/pen/mdmmWLG is the code; any pointers in the right direction would be greatly appreciated.
Update
Speaking generally, the problem was that I (being a newbie) didn't understand that the constructor was only run once. In the constructor of a child component I had this.cells = this.props.cells and then, in the render method, called this.cells.map( ... ). Since the constructor only ever ran once, this.cells was never updated after a state change.
It get duplicate ids somehow.
Looking at 3
pen.js:74 Looking at 4
pen.js:81 (4) [{…}, {…}, {…}, {…}]
pen.js:225 TEMPLATE PROPS: {rows: Array(4)}rows: Array(4)0: {cells: Array(0), id: 0}1: {cells: Array(1), id: 3}2: {cells: Array(0), id: 3}3: {cells: Array(0), id: 4}length: 4__proto__: Array(0)__proto__: Object
pen.js:232 row 0 is {cells: Array(0), id: 0}
pen.js:232 row 1 is {cells: Array(1), id: 3}
pen.js:232 row 2 is {cells: Array(0), id: 3}
pen.js:232 row 3 is {cells: Array(0), id: 4}
// in newRowButtonHandler
this.setState( { rows: [ ...this.state.rows, {cells:[], id: this.state.rows.length} ] } )
// try this
this.setState( { rows: [ ...this.state.rows, {cells:[], id: Math.round(Math.random() * 1000000)} ] }) )
UPD
Problem with id's, render and delete function use different set of values. https://codepen.io/Asmyshlyaev177/pen/yLbXBGo?editors=0010
Should not use e.target.id, should be single source of truth e.g. react state.
In Controls render you should add line const id = row.id; so it will be like this:
this.props.rows.map(
( row ) => {
const id = row.id;
let rowId = 'controlRow' + id
Using array idx is wrong. You should use idx from data structure.

React streamich / react-use upsert example

I would like to update a list in a react component using react-use upsert function.
const [aList, { upsert: upsertAList }] = useList({id: 0, name: 'Foo'});
The documentation say:
upsert: (predicate: (a: T, b: T) => boolean, newItem: T) => void; — Like updateFirst but in case of predicate miss - pushes item to the list.
But it doesn't help me.
Can anyone give me an example how to use it?
let elem = {id:1, name: 'Bar'}
I would like to upload the element in the list with the new one.
upsertAList(____) // <- what hould I write here instead of '____'?
Thanks in advice.
const [aList, { upsert: upsertAList }] = useList({id: 0, name: 'Foo'});
let repElem = {id: 0, name: 'bar'};
upsert( (a,b) => { if (a.id === b.id) return true }, newElem );

MongoDB: Query Builder to Update Field

I'm trying update a field in my mongodb collection with c# driver.
:: The Person Object
{
Name: 'Person1'
Prefers: [
{ Name: 'Prefer1', Location: 'Lisbon',
Sports: [ { Name: 'Sports1', Indoor: true, OutDoor: false } ]
]
}
My 'Sports' objects is a List of interface: ISports
How I update the field Person.Prefers.Sports.Name?
The next code don't work for me:
var filter = Builders<Person>.Filter.And(
Builders<Person>.Filter.Eq(i => i.Id, X),
Builders<Person>.Filter.ElemMatch(i => i.Prefers, i => i.Id == Y)
Builders<Person>.Filter.Eq("Person.Prefers.Sports.Id", Z)
);
var update = Builders<Person>.Update.Set("Prefers.Sports.$.Name", "Sports2");
UpdateOne(filter , update);
Thanks

Why tab space causes error in coffee script

I am learning react with rails from here. On following tutorial I canme to this error
ExecJS::RuntimeError in Records#index
SyntaxError: [stdin]:41:13: cannot have an implicit value in an implicit object
And then from little bit research on google, I came to know error is coming because of tab space.
Check this react js code:
#RecordForm = React.createClass
getInitialState: ->
title: ''
date: ''
amount: ''
render: ->
React.Dom.form
className: 'form-inline'
React.DOM.div
className: 'form-group'
React.DOM.input
type: 'text'
className: 'form-control'
placeholder: 'Date'
name: 'date'
value: #state.date
onChange: #handleChange
React.DOM.div
className: 'form-group'
React.DOM.input
type: 'text'
className: 'form-control'
placeholder: 'Title'
name: 'title'
value: #state.title
onChange: #handleChange
React.DOM.div
className: 'form-group'
React.DOM.input
type: 'number'
className: 'form-control'
placeholder: 'Amount'
name: 'amount'
value: #state.amount
onChange: #handleChange
React.DOM.button
type: 'submit'
className: 'btn btn-primary'
disabled: !#valid()
'Create record'
handleChange: (e) ->
name = e.target.name
#setState "#{ name }": e.target.value
valid: ->
#state.title && #state.date && #state.amount
handleSubmit: (e) ->
e.preventDefault()
$.post '', { record: #state }, (data) =>
#props.handleNewRecord data
#setState #getInitialState()
, 'JSON'
render: ->
React.DOM.form
className: 'form-inline'
onSubmit: #handleSubmit
Removing the spaces before 'Create record' from the above script will solve the problem. You can check it by this utility(link)
You should declare object in 2 ways and your compiler can't decide which of them you use.
The best way to avoid errors like yours is to choice one of the methods within each declaration.
# First way
obj =
a : 1
b : 2
# First way (compact)
obj = a : 1, b : 2
# Second way
# vars and props with same names (compact)
a = 1; b = 2
obj = {
a
b
}
# Second way (compact)
a = 1; b = 2
obj = {a, b}
You can also use React JSX support for Coffeescript
update:
Lets look closer to you code.
You can send two arguments like that.
React.DOM.button
type : 'submit'
className : 'btn btn-primary'
disabled : !#valid()
, 'Create record'
Or like that.
The combination of entering object and not object compiles into two arguments.
type : 'submit'
className : 'btn btn-primary'
disabled : !#valid()
'Create record'
And what you do in your code Compiler try to send Object {'Create record' : 'Create record'} as argument into result of call !#valid().
React.DOM.button
type: 'submit'
className: 'btn btn-primary'
disabled: !#valid()
'Create record'

Resources