question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Simple `mapStateToProps` conundrum

See original GitHub issue

Ok, here is the case. And please correct me if I’m trying to achieve this the “wrong way”.

I have an auth logic which looks something like this:

// logic/auth.js

export default koa({
  // ... actions, path, etc.
  reducers: ({ actions }) => ({
    user: [null, PropTypes.object, {
      [actions.setUser]: (_, user) => user,
    }],
  }),
});

Then I want to have a HOC which is called CheckPermissions and it should, given the permissionsCheckCallback, render children only if the provided function returns true.

Now, the way I would go about it with vanilla redux is to have the CheckPermissions component connected with the store by giving it a mapStateToProps function which would:

  • take the user from the provided state
  • take the permissionsCheckCallback function from ownProps
  • return { hasPermissions: permissionsCheckCallback(user) }

This way component is never rerendered even if the user changes but the permissions stay the same (optimal).

Now, I’ve started doing this in kea by having the CheckPermissions component (pure function really…) export this:

// components/CheckPermissions.jsx

import Auth from '../logic/auth';
import { connect } from 'kea';

export default connect({
  props: [
    Auth, [
      'user',
    ],
  ],
})((props) => (
  props.hasPermissions(props.user)
  ? props.children
  : null
))

But, the mapStateToProps concept is happening inside the component, not outside so it does not stop the “rerender” cycle: if the user changes - even if the permissions do not - it will trigger the render.

Now, either I’m not reading the docs right / not getting some core concept of kea or this is not possible to do the kea way? Sure, I can always target the user from the global state (no need to even connect Auth) but this is why I’m trying to use kea, so I don’t have to think about the “global” state and bring “logic” as needed (again - this may be wrong on many levels, and kea is not intended to be used this way).

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:15 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
mariusandracommented, Oct 21, 2017

Hey @nikolakanacki , an interesting situation you have there 😃

First, Kea is not meant to replace Redux at every instance everywhere. Sometimes, for some special components, it can make sense to revert back to Redux’s functions. You an access all the selectors of Auth just with something like user = Auth.selectors.user(state).

That said, there is one way you could achieve what you’re after. I haven’t tested it, so please let me know if it works.

First, whatever prop is in connect should trigger a re-render when it changes. As you specifically wish to avoid that for user, don’t pass it to connect, but manually pass the selector via Auth.selectors to selectors in kea({}). Something like this:

import Auth from '../logic/auth';
import { kea } from 'kea';

export default kea({
  selectors: ({ selectors }) => ({
    hasPermissions: [
      () => [(_, props) => props.hasPermissions, Auth.selectors.user],
      (hasPermissions, user) => hasPermissions(user),
      PropTypes.bool
    ]
  })
})((props) => (
  props.hasPermissions
  ? props.children
  : null
))

If I’m not mistaken, this should work, have access to the hasPermissions function in the props and to the user in Auth, without re-rendering when any of them change, but when the final result of the selector changes.

Then again, this is just a fancy abstraction over mapStateToProps, so it might be cleaner to just use it directly.

Cheers and let me know if this works 😃

0reactions
nikolakanackicommented, Oct 23, 2017

Yeah, just what I was thinking, so we get a clear picture of where the origin is. It was ~3 in the morning so I just rolled back but I think it had to do with something regarding the connect feature, as like it was expecting those loose useless parts of the store to be found in the store tree.

I’m currently creating a tiny (batteries included but optional) admin library at the moment featuring kea and antd, will get to this asap since I’m also looking forward to a fix.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Connect: Extracting Data with mapStateToProps - React Redux
The first argument to a mapStateToProps function is the entire Redux store state (the same value returned by a call to store.getState() )....
Read more >
Redux - Update store with same function from different files
I have multiple files, which need to update the store in exactly the same way, based on the stores current state. Currently I...
Read more >
If Your Refactors Break A Lot of Tests, You May Not Be Testing ...
Testing is supposed to validate that your app works. If you do some refactoring and your app still works but your tests are...
Read more >
Introducing redux-operations - Matt Krick
Concept #1: One action has a sequence of operations · Assign a priority to each operation so they occur in the order we...
Read more >
Redux Setup for Your React App - Better Programming
This guide will cover a basic and simple setup for your React app. ... the App component, let's create a function called mapStateToProps...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found