How to use flat-list in re-natal? - react-native

I have the following flatlist:
[flat-list {
:data [{:name "a"}
{:name "b"}
{:name "c"}]
:render-item (fn [item] [text (:name item)])
:key-extractor #(random-uuid)
}
]
But it's not working and gives me:
Invariant Violation: Objects are not valid as a React child (found: object with keys {name, id, class}). If you meant to render a collection of children, use an array instead.
What am I doing wrong?
-- EDIT --
Now I have the following:
[flat-list {
:data [{:name "a"}
{:name "b"}
{:name "c"}]
:render-item (fn [item-]
(r/reactify-component
[text (:name item-)]
))
:key-extractor #(:name %)
:content-container-style {:align-items "center"}
}
]
But I get the error: Functions are not valid as a React child. This may happen if you returns a Component instead of <Component/> from render. Or maybe you meant to call this function rather than return it.
Using as-element instead doesn't give the error but also doesn't render the text components.

It's just because renderItem waits a react component
:render-item (fn [item]
(r/reactify-component [text (:name item)]))
:render-item (fn [item]
(r/as-element [text (:name item)]))

Related

Reagent performance issue when passing atom as a function parameter

I work on the react-native application using Clojurescript re-frame and reagent. I have one text input component, and have two versions of the code:
Version 1: input text is a separate component, and state atom is passed as an argument, the same as the recommended in the reagent library docs and examples.
(defn todo-input [value]
[rn/text-input
{:style (styles :textInput) :multiline true
:placeholder "What do you want to do today?" :placeholder-text-color "#abbabb"
:value #value
:on-change-text #(reset! value %)}]
)
(defn todo-screen []
(let [value (r/atom nil)]
[rn/view {:style (styles :container)}
[rn/text {:style (styles :header)} "Todo List"]
[rn/view {:style (styles :textInputContainer)}
[todo-input value]
[rn/touchable-opacity
{:on-press (fn [] (rf/dispatch [:add-todo #value]) (reset! value nil))}
[icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]
[todos]
]))
Version 2: everything in one component.
(defn todo-screen []
(let [value (r/atom nil)]
[rn/view {:style (styles :container)}
[rn/text {:style (styles :header)} "Todo List"]
[rn/view {:style (styles :textInputContainer)}
[rn/text-input
{:style (styles :textInput) :multiline true
:placeholder "What do you want to do today?" :placeholder-text-color "#abbabb"
:value #value
:on-change-text #(reset! value %)}]
[rn/touchable-opacity
{:on-press (fn [] (rf/dispatch [:add-todo #value]) (reset! value nil))}
[icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]
]]
[todos]]))
The issue is that first version has a performance issue while typing, since there's a big delay and flickering when trying to type fast. Version 2 doesn't have any issues, and I can type as fast as I can.
According to the reagent documentation, passing r/atom as a parameter should not incur any performance issues.
Am I doing something wrong here? What would be the best way to avoid performance penalty here.
This is a small example, and having one component instead of two is not a big deal, but splitting one big to multiple smaller components in a good praxis. State here is local to the component, and I don't want to use re-frame for it.
re-frame/dispatch puts your events in a queue for re-frame to process, so there can be a slightly delay before it actually goes through and your change will be there.
Sounds like you're experiencing the same issue stated here: https://github.com/day8/re-frame/issues/368
So one work around is to use re-frame.core/dispatch-sync which forces re-frame to handle the event directly and synchronously. You might have to also add a call to reagent.core/flush to force re-render the component. I haven't needed flush before when building web clients, but React Native seems to work differently.
You can read more about these two functions here:
https://github.com/day8/re-frame/blob/master/src/re_frame/router.cljc#L251-L263
https://github.com/reagent-project/reagent/blob/master/src/reagent/impl/batching.cljs
Mentioned in the issue above is also https://github.com/Day8/re-com that supposedly works around the issue somehow, but I didn't take a closer look at that.
Your solution #2 is not wrong either, it just gives you a different way of working. So if you need the data in your app-db to update on every keypress for example, only something more like #1 will work. Or using solution #2 but passing in the atom as an argument to your component.
Both of your versions have an issue. You should use Form-2 type components when using local state. Like this:
(defn todo-screen []
(let [value (r/atom nil)]
(fn []
[rn/view {:style (styles :container)}
[rn/text {:style (styles :header)} "Todo List"]
[rn/view {:style (styles :textInputContainer)}
[todo-input value]
[rn/touchable-opacity
{:on-press (fn [] (rf/dispatch [:add-todo #value]) (reset! value nil))}
[icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]
[todos]])))
More info about Form-2 here.
Or you could use the r/with-let instead. More info about with-let.
Regarding your original question, you could have a compromise between both your versions, and extract the input and submit button into a separate component:
(defn todo-input-container [on-press]
(r/with-let [value (r/atom nil)]
[rn/view {:style (styles :textInputContainer)}
[rn/text-input
{:style (styles :textInput) :multiline true
:placeholder "What do you want to do today?" :placeholder-text-color "#abbabb"
:value #value
:on-change-text #(reset! value %)}]
[rn/touchable-opacity
{:on-press (fn []
(on-press #value)
(reset! value nil))}
[icon {:name "plus" :size 30 :color "blue" :style {:margin-left 15}}]]]))
(defn todo-screen []
[rn/view {:style (styles :container)}
[rn/text {:style (styles :header)} "Todo List"]
[todo-input-container (fn [value] (rf/dispatch [:add-todo value]))]
[todos]])
Looks like the issue here is that reagent doesn't support well components with the controlled inputs due to its async nature.
Controlled input (via :value) should be avoided, or worked around by forcing component update immediately after changing :value.
See reagent issue and explanation for more details.

Test pre-requisites for tabular tests; how does tabular work?

Let's say I am attempting to test an api that is supposed to handle presence or absence of certain object fields.
Let's say I have tests like so:
(def without-foo
{:bar "17"})
(def base-request
{:foo "12"
:bar "17"})
(def without-bar
{:foo "12"})
(def response
{:foo "12"
:bar "17"
:name "Bob"})
(def response-without-bar
{:foo "12"
:bar ""
:name "Bob"})
(def response-without-foo
{:bar "17"
:foo ""
:name "Bob"})
(facts "blah"
(against-background [(external-api-call anything) => {:name => "Bob"})
(fact "base"
(method-under-test base-request) => response)
(fact "without-foo"
(method-under-test without-foo) => response-without-foo)
(fact "without-bar"
(method-under-test without-bar) => response-without-bar))
This works as you would expect and the tests pass. Now I am attempting to refactor this using tabular like so:
(def request
{:foo "12"
:bar "17"})
(def response
{:foo "12"
:bar "17"
:name "Bob"})
(tabular
(against-background [(external-api-call anything) => {:name "Bob"})]
(fact
(method-under-test (merge request ?diff) => (merge response ?rdiff))
?diff ?rdiff ?description
{:foo nil} {:foo ""} "without foo"
{} {} "base case"
{:bar nil} {bar ""} "without bar")
Which results in:
FAIL at (test.clj:123)
Midje could not understand something you wrote:
It looks like the table has no headings, or perhaps you
tried to use a non-literal string for the doc-string?
Ultimately I ended up with:
(tabular
(fact
(method-under-test (merge request ?diff) => (merge response ?rdiff) (provided(external-api-call anything) => {:name "Bob"}))
?diff ?rdiff ?description
{:foo nil} {:foo ""} "without foo"
{} {} "base case"
{:bar nil} {bar ""} "without bar")
Which passes. My question is. How does the tabular function differ from the facts function, and why does one of them accept an against-background while the other blows up?
You need to have following nesting if you want to establish background prerequisites for all your tabular based facts:
(against-background [...]
(tabular
(fact ...)
?... ?...))
For example:
(require '[midje.repl :refer :all])
(defn fn-a []
(throw (RuntimeException. "Not implemented")))
(defn fn-b [k]
(-> (fn-a) (get k)))
(against-background
[(fn-a) => {:a 1 :b 2 :c 3}]
(tabular
(fact
(fn-b ?k) => ?v)
?k ?v
:a 1
:b 3
:c 3))
(check-facts)
;; => All checks (3) succeeded.
If you want to have a background prerequisite per each tabular case you need to nest it as following:
(tabular
(against-background [...]
(fact ...))
?... ?...)
It's important to have the table just under tabular level, not nested in against-background or fact.
For example:
(require '[midje.repl :refer :all])
(defn fn-a []
(throw (RuntimeException. "Not implemented")))
(defn fn-b [k]
(-> (fn-a) (get k)))
(tabular
(against-background
[(fn-a) => {?k ?v}]
(fact
(fn-b ?k) => ?v))
?k ?v
:a 1
:b 2
:c 3)
(check-facts)
;; => All checks (3) succeeded.
In your code it looks like the tabular data is not positioned correctly (parentheses, brackets and curly braces are not balanced correctly so it's impossible to say what exactly is incorrect).

mocking a function call in midje

Say I have a function
(defn extenal_api_fn [stuff]
... do things....
)
(defn register_user [stuff]
(external_api_fn stuff))
And then a test
(def stuff1
{:user_id 123 })
(def stuff2
{:user_id 234})
(background (external_api_fn stuff1) => true
(with-redefs [external_api_fn (fn [data] (println "mocked function"))]
(register_user stuff1) => true)
(register_user stuff2) => true)
(facts "stuff goes here"
(fact "user that registers correctly
(= 1 1) => truthy)
(fact "user that has a registration failure"
(= 1 2) => falsy))
This fails with
"you never said #'external_api_fn" would be called with these arguments:
contents of stuff1
What would be a good way to stub this function call (in only some cases) in order to simulate an internal transaction failure.
You could use Midje's provided:
(fact
(register_user stuff1) => :registered
(provided
(extenal_api_fn stuff1) => :registered))
(fact
(register_user stuff2) => :error
(provided
(external_api_fn stuff2) => :error))
You can also stub a function to return a value no matter input parameters by using anything in place of the function argument:
(fact
(register_user stuff2) => :error
(provided
(external_api_fn anything) => :error))

Missing translations in a collection on initialize

I am trying to add I18n on a collection in one of my models. When I initialize the server/console and I called the collection, the translation is missing, but after reloading the app or the console, the translations appear.
Here is the example:
[1] pry(main)> Listing::CONDITIONS
=> [["translation missing: en.activerecord.attributes.listing.conditions.new",
"new"], ["translation missing: en.activerecord.attributes.listing.conditions.used-new",
"used-new"], ["translation missing: en.activerecord.attributes.listing.conditions.used-vgood",
"used-vgood"], ["translation missing: en.activerecord.attributes.listing.conditions.used-good",
"used-good"], ["translation missing: en.activerecord.attributes.listing.conditions.used-acceptable",
"used-acceptable"]]
[2] pry(main)> reload!
Reloading...
=> true
[3] pry(main)> Listing::CONDITIONS
=> [["New", "new"],
["Used - Like New", "used-new"],
["Used - Very Good", "used-vgood"],
["Used - Good", "used-good"],
["Used - Acceptable", "used-acceptable"]]
Any ideas?

Tiered? structure of complex Zend Framework models/objects

What is the best way to store complex models in ZF? In the example below, should each attribute be a separate model entirely, or should the item be a multi dimensional array (as shown below)?
object(Application_Model_Item)#79 (4) {
["_id":protected] => int(45)
["_name":protected] => string(5) "Bolts"
["_description":protected] => NULL
["_attributes":protected] => array(2) {
[0] => array(2) {
["id"] => string(1) "3"
["name"] => string(4) "Size"
}
[1] => array(2) {
["id"] => string(1) "4"
["name"] => string(6) "Length"
}
}
Thanks in advance.
It all depends on your use case:
Indexing by ID or Position:
If you would like speed when accessing a particular attribute, then index the attributes by their IDs instead of their index position.
If you would like to keep an order, then order them by index position and a position offset amount.
Independent Table Vs Local Array:
If the attributes are duplicated in multiple items, then have them as their own table, and reference the attributes to that table.
If the attributes are not refenced and are unique to each item, then using them as serialise-able arrays (for storage) is adequate than needing them to be their own table.
In the case of _attributes i would use array of objects.
So it attributes would be an array of new model Attribute()
I make a class for every business model entity
["_attributes":protected] => array(2) {
[0] => Object(Model_Attribute) {}
[1] => Object(Model_Attribute) {}
}
class Model_Attribute {
protected $id;
public function getId();
public function setId($id);
.
.
.
}
I suggest you look at Doctrine ORM 2.0 since it can support the design from the above.
Look at this page, it may give you a clue:
http://www.doctrine-project.org/projects/orm/2.0/docs/reference/association-mapping/en