Fork me on GitHub

What's the canonical way of deleting an entry in normalized data?

{:list/one [[:person/by-name "John"] [:person/by-name "Mary"] [:person/by-name "Bob"]],
 :list/two [[:person/by-name "Mary"] [:person/by-name "Gwen"] [:person/by-name "Jeff"]],
 {"John" {:name "John", :points 0},
  "Mary" {:name "Mary", :points 1, :age 27},
  "Bob" {:name "Bob", :points 0},
  "Gwen" {:name "Gwen", :points 0},
  "Jeff" {:name "Jeff", :points 0}}}
This is taken from the Wiki example. In a mutate function operating on the data, how would I remove a person? An an example, just removing it from the :persons/by-name key leaves it in the :list/one/`:list/two` keys so the UI still renders it.


It feels like the mutate function shouldn't have to remove it everywhere it's featured...that's the point of normalisation, right?


@acron: I think you just have to remove it from :persons/by-name and every refs list that points to that table, just as you say. Seems sensible to create functions to do this for you. Here's the generic delete I use. It only removes from one referencing place, so needs improving for your case.


thanks @cjmurphy. As a relevant question, is normalisation mandatory? Seems like I'm spending a lot of time fighting it


Not mandatory. I think in terms of yes spend a lot of time with it, but activities are just different to other types of programming. It is like mutations are the only serious programming that needs to be done.


Not mandatory but anything else would be for a very special use case I think.


@cjmurphy: Ok, I'll stick with it for now...your fn has helped a lot so thanks


The other trick, of course, is persuading the UI element to re-render once removing the Person


For removing a person from many then that's not too tricky - the transact! has to be where this is the parent component. Follow on reads a bit more difficult for me: I often use idents as follow on reads, just to specifically re-render a particular component. I ought to use keywords - in fact ought to replace all those uses with keywords.


Sometimes having two components sharing the same ident can be helpful too.


@cjmurphy: That's an interesting nuance I wasn't aware of - transact!s need to always be on the parent UI?


When deleting they need to be, because delete is an operation of the whole list.


Also add is an operation of the whole list.

acron16:06:13 even using :value in a mutate isn't enough to persuade a parent to re-render?


:value doesn't mean anything - only the :action does.


Ok, good to know


Yep - that's asked all the time here - 'just for documentation' is the answer for the purpose being :value


Makes it hard to feature a 'delete me' element on a UI element then


Not so hard because you have computed to pass whatever you need down to the child.


Either a function or the parent itself can be passed in computed.


Yeah, that's a good point.


@anmonteiro: re: path navigation: Sorry for the late answer. I'm using a bidi+pushy approach and creating mutations depending on the url structure. That part is alright. It seems like path navigation is a separate concern than routing (if done right). My biggest problem with routing has been sending the correct query to the remote(s). When I've gone with either subquery or set-query! solutions, the full-query has depended on props, and when gather-sends is called after a transact! with a route change mutation, the query hasn't been up-to-date. So I've had to transact! route changes, then - possibly multiple - set-query!, then a last transact! to gather-sends and using different hacks to prevent multiple requests. I can now imagine a solution with unions that doesn't have problems with getting the remote query correct. I'll try to re-write our routing with unions and then probably transition to your lib. I'll let you know how it goes! Thanks ✌️