npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2019 – Ryan Hefner

@casl/react

v1.0.4

Published

React component for CASL which makes it easy to add permissions in any React application

Downloads

47,032

Readme

CASL React @casl/react NPM version CASL Documentation CASL Join the chat at https://gitter.im/stalniy-casl/casl

This package allows to integrate @casl/ability into React application. So, you can show or hide UI elements based on user ability to see them.

Installation

npm install @casl/react @casl/ability

Getting Started

This package provides Can component which can be used to conditionally show UI elements based on user abilities. This component accepts children and 4 properties (see Property names and aliases)

  • I (do is an alias) - name of the action and field (e.g., read, update or read title).\ Note: action names are not allowed to have spaces (e.g., this is invalid action send email) because <Can> component expects that the 2nd word in action is a field name which needs to be checked.
  • a (on, of, this are aliases) - checked subject
  • not - checks whether the ability does not allow an action
  • ability - an instance of Ability which will be used to check permissions

children property may be either a render function (a recommended way):

<Can I="create" a="Post" ability={ability}>
  {() => <button onClick={this.createPost.bind(this)}>Create Post</button>}
</Can>

or React elements:

<Can I="create" a="Post" ability={ability}>
  <button onClick={this.createPost.bind(this)}>Create Post</button>
</Can>

Note: it's better to pass children as a render function just because it will not create additional React elements if user doesn't have ability to do some action (in the case above read Post)

1. Scoping Can to use a particular ability

Yes, it's a bit inconvenient to pass ability in every Can component. This was actually done for cases when you have several abilities in your app and/or want to restrict a particular Can component to check abilities using another instance.

There are 2 function which allow to scope Can to use a particular instance of Ability:

  • createCanBoundTo
  • createContextualCan

The first function just creates a new component which is bound to a particular ability and accepts only 2 properties: do and on:

// Can.js
import { createCanBoundTo } from '@casl/react'
import ability from './ability'

export default createCanBoundTo(ability)

Then import bound Can into any component (now you don't need to pass ability property):

import Can from './Can'

export function button() {
  return (
    <Can I="create" a="Post">
      {() => <button onClick={this.createPost.bind(this)}>Create Post</button>}
    </Can>
  )
}

createContextualCan uses React Context API (available from [email protected]) and provides specified ability to all children components.

// ability-context.js
import { createContext } from 'react'
import { createContextualCan } from '@casl/react'

export const AbilityContext = createContext()
export const Can = createContextualCan(AbilityContext.Consumer)

Later you need to provide your ability to AbilityContext.Provider

import { AbilityContext } from './ability-context'
import ability from './ability'

export default function App({ props }) {
  return (
    <AbilityContext.Provider value={ability}>
      <TodoApp />
    </AbilityContext.Provider>
  )
}

And inside TodoApp you can use previously created Can component:

import React, { Component } from 'react'
import { Can } from './ability-context'

export class TodoApp extends Component {
  createTodo() {
    // ....
  }

  render() {
    return (
      <Can I="create" a="Todo">
        {() => <button onClick={this.createTodo.bind(this)}>Create Todo</button>}
      </Can>
    )
  }
}

Alternatively you may use React's contextType component property to set context for the component:

class MyComponent extends PureComponent {
  // ...

  render() {
    // this.context is a provided Ability instance
    return this.context.can('manage', 'Todo') ? <TodoApp /> : null
  }
}

MyComponent.contextType = AbilityContext

See casl-react-example for more examples.

2. Defining Abilities

There are 2 options how you can define Ability instance:

  • define an empty ability and update it when user login
  • define ability using AbilityBuilder and optionally update it when user login

To define empty ability:

// ability.js
import { Ability } from '@casl/ability'

export default new Ability([])

To create ability using AbilityBuilder

// ability.js
import { AbilityBuilder } from '@casl/ability'

export default AbilityBuilder.define(can => {
  can('read', 'all')
  // ....
})

Later in your login component:

import React, { Component } from 'react'
import ability from './ability'

export class LoginComponent extends Component {
  login(event) {
    event.preventDefault()
    const { email, password } = this.state

    return fetch('path/to/api/login', { method: 'POST', body: JSON.stringify({ email, password }) })
      .then(response => response.json())
      .then(session => ability.update(session.rules))
  }

  render() {
    return (
      <form onSubmit={this.login.bind(this)}>
        // ...
      </form>
    )
  }
}

Obviously, in this case your server API should provide the list of user abilities in rules field of the response. See @casl/ability package for more information on how to define abilities.

3. Property names and aliases

As you can see from the code above, component name and its property names and values creates an English sentence, basically a question. For example, the code below reads as Can I create a Post:

<Can I="create" a="Post">
  {() => <button onClick={...}>Create Post</button>}
</Can>

There are several other property aliases which allow to construct a readable question (all possible combinations of readable alias names can be found in Typescript definitions:

  • use a (or an) alias when you check by Type
<Can I="read" a="Post">...</Can>
  • use of alias instead of a when you check by subject field
<Can I="read title" of="Post">...</Can>

// or when checking on instance. `this.props.post` is an instance of `Post` class (i.e., model instance)

<Can I="read title" of={this.props.post}>...</Can>
  • use this alias instead of of and a when you check action on instance
// `this.props.post` is an instance of `Post` class (i.e., model instance)

<Can I="read" this={this.props.post}>...</Can>
  • use do and on if you are bored and don't want to make your code more readable :)
// `this.props.post` is an instance of `Post` class (i.e., model instance)

<Can do="read" on={this.props.post}>...</Can>

// or per field check
<Can do="read title" on={this.props.post}>...</Can>
  • use not when you want to invert the render method
<Can not I="read" a="Post">...</Can>
  • use passThrough if you want to customize behavior of <Can> component, for example disable button instead of hiding it:
<Can I="read" a="Post" passThrough>
  {can => <button disabled={!can}>Save</button>}
</Can>

4. Usage with React hooks

Sometimes logic in the component may be a bit complicated, so you can't use <Can> component. In such cases, you can use new React useContext hook:

function MyComponent() {
  const ability = useContext(AbilityContext)

  return ability.can('manage', 'Todo') ? <TodoApp /> : null
}

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing

License

MIT License