• This repository has been archived on 10/Oct/2023
  • Stars
    star
    218
  • Rank 181,805 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 8 years ago
  • Updated about 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🎩 A React Redux toolset for the WordPress API

kasia

A React Redux toolset for the WordPress API

Made with ❀ at @outlandish

npm version travis ci build Coverage Status


✨ We welcome contributors!

🚦 Issues are triaged using a traffic light system:

Β Β Β #00ff00 small - quick tasks, great for beginner contributors
Β Β Β #ffff00 medium - tasks with increased complexity, may take some time to implement
Β Β Β #ff0000 large - big tasks that touch many parts of the library, will require commitment

Get started contributing here.


Get data from WordPress and into components with ease...

// e.g. Get a post by its slug
@connectWpPost('post', 'spongebob-squarepants')
export default class extends React.Component () {
  render () {
    const { post: spongebob } = this.props.kasia
    
    if (!spongebob) {
      return <p>{'Who lives in a pineapple under the sea?'}</p>
    }
    
    return <h1>{spongebob.title.rendered}!</h1>
    //=> Spongebob Squarepants!
  }
}

Features

  • Declaratively connect React components to data from WordPress.
  • Uses node-wpapi in order to facilitate complex queries.
  • Register and consume Custom Content Types with ease.
  • All WP data is normalised at store.wordpress, e.g. store.wordpress.pages.
  • Support for universal applications.
  • Support for plugins, e.g. wp-api-menus.

Glossary

Requirements

Kasia suits applications that are built using these technologies:

Install

npm install kasia --save
yarn add kasia

Import

// ES2015
import kasia from 'kasia'
// CommonJS
var kasia = require('kasia')

Configure

Configure Kasia in three steps:

  1. Initialise Kasia with an instance of node-wpapi.

  2. Spread the Kasia reducer when creating the redux root reducer.

  3. Run the Kasia sagas after creating the redux-saga middleware.

A slimline example...

import { combineReducers, createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import kasia from 'kasia'
import wpapi from 'wpapi'

const wpai = new wpapi({ endpoint: 'http://wordpress/wp-json' })

const { kasiaReducer, kasiaSagas } = kasia({ wpapi })

const rootSaga = function * () {
  yield [...kasiaSagas]
}

const rootReducer = combineReducers({
  ...kasiaReducer
})

const sagaMiddleware = createSagaMiddleware()

export default function configureStore (initialState) {
  const store = createStore(
    rootReducer,
    initialState,
    applyMiddleware(sagaMiddleware)
  )
  
  sagaMiddleware.run(rootSaga)

  return store
}

Usage

kasia(options) : Object

Configure Kasia.

  • options {Object} Options object

Returns an object containing the Kasia reducer and sagas.

const { kasiaReducer, kasiaSagas } = kasia({
  wpapi: new wpapi({ endpoint: 'http://wordpress/wp-json' })
})

The options object accepts:

  • wpapi {wpapi}

    An instance of node-wpapi.

  • keyEntitiesBy {String} (optional, default='id')

    Property of entities that is used to key them in the store.

    One of: 'slug', 'id'.

  • debug {Boolean} (optional, default=false)

    Log debug information to the console.

  • contentTypes {Array} (optional)

    Array of custom content type definitions.

    // Example custom content type definition
    contentTypes: [{
      name: 'book',
      plural: 'books',
      slug: 'books',
      route, // optional, default="/{plural}/(?P<id>)"
      namespace, // optional, default="wp/v2"
      methodName // optional, default={plural}
    }]
  • plugins {Array} (optional)

    Array of Kasia plugins.

    import kasiaWpApiMenusPlugin from 'kasia-plugin-wp-api-menus'
    
    // Example passing in plugin
    plugins: [
        [kasiaWpApiMenusPlugin, { route: 'menus' }], // with configuration
        kasiaWpApiMenusPlugin, // without configuration
    ]

Decorators

Things to keep in mind:

  • A component will make a request for data 1) when it mounts and 2) if its props change. For connectWpPost a change in props will trigger Kasia to try and find entity data for the new identifier in the store. If it is found, no request is made.
  • Content data should be parsed before being rendered as it may contain encoded HTML entities.
  • In arbitrary queries with connectWpQuery, we suggest that you always call the embed method on the query chain, otherwise embedded content data will be omitted from the response.
  • Paging data for the request made on behalf of the component is available at this.props.kasia.query.paging.
  • The examples given assume the use of decorators (sometimes called annotations). However decorator support is not necessary. See the end of each example for the alternative Higher Order Component approach.

@connectWpPost(contentType, identifier) : Component

Connect a component to a single entity in WordPress, e.g. Post, Page, or custom content type.

  • contentType {String} The content type to fetch
  • identifier {String|Number|Function} ID of the entity to fetch or function that derives it from props

Returns a connected component.

Example, using identifier derived from route parameter on props:

import React, { Component } from 'react'
import { Route } from 'react-router'
import { connectWpPost } from 'kasia/connect'
import { Page } from 'kasia/types'

@connectWpPost(Page, (props) => props.params.slug)
export default class Page extends Component {
  render () {
    const { query, page } = this.props.kasia

    if (!query.complete) {
      return <span>Loading...</span>
    }

    return <h1>{page.title}</h1>
  }
}

// Without decorator support
export default connectWpPost(Page, (props) => props.params.slug)(Post)

@connectWpQuery(queryFn, shouldUpdate) : Component

Connect a component to the result of an arbitrary WP-API query. Query is always made with ?embed query parameter.

  • queryFn {Function} Function that accepts args wpapi, props, state and should return a WP-API query
  • shouldUpdate {Function} Called on componentWillReceiveProps with args thisProps, nextProps, state

Returns a connected component.

The component will request new data via queryFn if shouldUpdate returns true.

Entities returned from the query will be placed on this.props.kasia.entities under the same normalised structure as described in The Shape of Things.

Example, fetching the most recent "News" entities:

import React, { Component } from 'react'
import { Route } from 'react-router'
import { connectWpPost } from 'kasia/connect'

// Note the invocation of `embed` in the query chain
@connectWpQuery((wpapi, props) => {
  return wpapi.news().month(props.month).embed().get()
}, (thisProps, nextProps) => thisProps.month != nextProps.month)
export default class RecentNews extends Component {
  render () {
    const {
      query,
      data: { news }
    } = this.props.kasia

    if (!query.complete) {
      return <span>Loading...</span>
    }

    return (
      <div>
        <h1>Recent News Headlines</h1>
        {Object.keys(news).map((key) =>
          <h2>{news[key].title}</h2>)}
      </div>
    )
  }
}

// Without decorator support
export default connectWpQuery((wpapi) => {
  return wpapi.news().embed().get()
})(Post)

Exports

kasia

The Kasia configurator and preload utilities.

import kasia, { preload, preloadQuery } from 'kasia'

kasia/connect

The connect decorators.

import { connectWpPost, connectWpQuery } from 'kasia/connect'

kasia/types

The built-in WordPress content types that can be passed to connectWpPost to define what content type a request should be made for.

import {
  Category, Comment, Media, Page,
  Post, PostStatus, PostType,
  PostRevision, Tag, Taxonomy, User
} from 'kasia/types'

See Universal Application Utilities for more details.

Plugins

Kasia exposes a simple API for third-party plugins.

A plugin should:

  • be a function that accepts these arguments:

    • wpapi {wpapi} An instance of wpapi
    • pluginOptions {Object} The user's options for the plugin
    • kasiaOptions {Object} The user's options for Kasia
  • return an object containing reducers (Object) and sagas (Array).

  • use the 'kasia/' action type prefix.

// Example definition returned by a plugin
{
  reducer: {
    'kasia/SET_DATA': function setDataReducer () {}
    'kasia/REMOVE_DATA': function removeDataReducer () {}
  },
  sagas: [function * fetchDataSaga () {}]
}

A plugin can hook into Kasia's native action types, available at kasia/lib/constants/ActionTypes. All reducers for an action type are merged into a single function that calls each reducer in succession with the state returned by the previous reducer. This means the order of plugins that touch the same action type is important.

Available plugins:

Please create a pull request to get your own added to the list.

Universal Applications

Important...

  • before calling the preloaders for SSR you must call kasia.rewind()
  • or if you call runSagas() from the utilities then this is done for you.

Utilities

runSagas(store, sagas) : Promise

Run a bunch of sagas against the store and wait on their completion.

  • store {Object} Redux store enhanced with runSaga method
  • sagas {Array} Array of functions that accept the store state and return a saga generator

Returns a Promise resolving on completion of all the sagas.

preload(components[, renderProps][, state]) : Generator

Create a saga operation that will preload all data for any Kasia components in components.

  • components {Array} Array of components
  • [renderProps] {Object} (optional) Render props object derived from the matched route
  • [state] {Object} (optional) Store state

Returns a saga operation.

preloadQuery(queryFn[, renderProps][, state]) : Generator

Create a saga operation that will preload data for an arbitrary query against the WP API.

  • queryFn {Function} Query function that returns node-wpapi query
  • [renderProps] {Object} (optional) Render props object
  • [state] {Object} (optional) Store state

Returns a saga operation.

<KasiaConnectedComponent>.preload(renderProps[, state]) : Array<Array>

Connected components expose a static method preload that produces an array of saga operations to facilitate the request for entity data on the server.

  • renderProps {Object} Render props object derived from the matched route
  • [state] {Object} (optional) State object (default: null)

Returns an array of saga operations.

Saga Operation Signature

A saga operation is an array of the form:

[ sagaGeneratorFn, action ]

Where:

  • sagaGenerator Function that must be called with the action.

  • action action Object containing information for the saga to fetch data.

Example

A somewhat contrived example using the available preloader methods.

import { match } from 'react-router'
import { runSagas, preload, preloadQuery } from 'kasia'

import routes from './routes'
import store from './store'
import renderToString from './render'
import getAllCategories from './queries/categories'

export default function renderPage (res, location) { 
  return match({ routes, location }, (error, redirect, renderProps) => {
    if (error) return res.sendStatus(500)
    if (redirect) return res.redirect(302, redirect.pathname + redirect.search)
    
    // We are using `runSagas` which rewinds for us, but if we weren't then
    // we would call `kasia.rewind()` here instead:
    //
    // kasia.rewind()
    
    // Each preloader accepts the state that may/may not have been modified by
    // the saga before it, so the order might be important depending on your use-case!
    const preloaders = [
      () => preload(renderProps.components, renderProps),
      (state) => preloadQuery(getAllCategories, renderProps, state)
    ]
    
    return runSagas(store, preloaders)
      .then(() => renderToString(renderProps.components, renderProps, store.getState()))
      .then((document) => res.send(document))
  })  
}

Testing

Kasia components can be tested by:

  • lifting the query function from the connectWpQuery decorator and exporting it
  • lifting the should update function from the connectWpQuery deocrating and exporting it
  • making your component available as a named export prior to decoration

An example:

export function postQuery (wpapi) {...}

export function shouldUpdate (thisProps, nextProps, state) {...}

export class Post extends Component {...}

@connectWpQuery(postQuery, shouldUpdate)
export default Post

You can then test the component without decoration, the query and the shouldUpdate function in isolation.

Contributing

All pull requests and issues welcome!

  • When submitting an issue please provide adequate steps to reproduce the problem.
  • PRs must be made using the standard code style.
  • PRs must update the version of the library according to semantic versioning.

If you're not sure how to contribute, check out Kent C. Dodds' great video tutorials on egghead.io!

Author & License

kasia was created by Outlandish and is released under the MIT license.

More Repositories

1

wpackagist

WordPress Packagist β€” manage your plugins with Composer
PHP
702
star
2

sync

Sync directory contents over HTTP using PHP
PHP
56
star
3

oowp

OOWP is a tool for WordPress theme developers that makes templating in WordPress more sensible.
PHP
41
star
4

kasia-boilerplate

🎩 A universal application boilerplate with Kasia
JavaScript
16
star
5

routemaster

Improved routing for WordPress
PHP
13
star
6

wp-api-response-modify

Makes WP API response JSON sensible
JavaScript
8
star
7

nodejs-mean-with-brunch-sass-passport

An Outlandish Example MEAN Web Application using Brunch, Sass, and Passport
JavaScript
8
star
8

OowpBundle

Object Oriented WordPress
PHP
7
star
9

poppler-build

A convenience repository to build and host poppler-utils for use in AWS Lambda functions
5
star
10

calendar-event-sync

WordPress plugin that saves Google Calendar events to your WordPress database as posts
PHP
5
star
11

outlandish-smtp

Provides a number of different ways to set how WordPress sends emails
PHP
4
star
12

social-monitor

PHP
4
star
13

RoutemasterBundle

Use WordPress as the CMS backend for your Symfony application
PHP
4
star
14

oowp-boilerplate

Boilerplate for OOWP
PHP
3
star
15

RestBundle

REST API for your Doctrine entities
PHP
2
star
16

docker-nginx-vts

Docker image for NGINX that includes VTS module
Dockerfile
2
star
17

i18n-outlandish-app

An Example of Internationalisation using angular-translate, Express, and Node.
JavaScript
2
star
18

AcfOowpBundle

PHP
2
star
19

docker-php-socialmonitor

Docker image for PHP for Social Monitor project
1
star
20

AcadOowpBundle

PHP
1
star
21

docker-wordpress-dev

Outlandish Docker image for WordPress with dev tools
Dockerfile
1
star
22

outlandish-wonolog

MU-plugin to setup Wonolog for Outlandish projects
PHP
1
star
23

php-crud-api-secure

A secure-by-default wrapper around https://github.com/mevdschee/php-crud-api
PHP
1
star
24

OrmLiteBundle

Persist large datasets efficiently using Doctrine mapping configuration
PHP
1
star