Integrating ReactJS into SharePoint

Linus Falck-Ytter

Boston Code Camp, April 7th, 2018

Software Developer @ MIT Sloan

@lifayt

slides.com/lifayt

github.com/lifayt

 

 

Pop Quiz

Pop Quiz

`Most Dreaded`

SharePoint Development

  • Master Page/Page Layout
  • Web Parts
  • Bundled Solutions & Apps
  • Content Editors
  • Script Editors
  • Iframes(?)
  • File Deployment
    • WSPs
    • File Libraries

SharePoint Web Development

Content Editor:

  • Links to an external Markup Document
  • Allows manipulation of Text & Markup wysiwyg style

Script Editor:

  • Write JavaScript directly onto the page

SharePoint Web Development

Content Editor:

  • Links to an external Markup Document
  • Allows manipulation of Text & Markup wysiwyg style

Script Editor:

  • Write JavaScript directly onto the page

SharePoint Web Development

SharePoint Document Library:

  • Deployable from anywhere
  • No downtime
  • Site-wide(ish)

Deployed Solution:

  • Visual Studio & Package
  • Client side & server side 
  • Downtime
  • Site-wide

SharePoint Web Development

SharePoint Document

Library:

  • Deployable from anywhere
  • No downtime
  • Site-wide

Deployed Solution:

  • Visual Studio & Package
  • Client side & server side
  • Downtime
  • Site-wide

Working with Client Side Code

SharePoint Encourages:

  • Editing directly on the file system
  • Injecting Javascript directly via a script editor
  • Working within the SharePoint environment

Modern JavaScript development mostly happens in a local development environment, with tooling to help the build and development process

Why React?

  • Fast and Fun
  • Instills a helpful mindset for data flow
  • Marketable skillset
  • I want to fit in with the cool kids

Before

Sharepoint 2016 Feature Pack 2

I want to use React

  • Add Libraries to SharePoint
  • Write ES5 React Code directly in SharePoint
  • Result:
  • Build your own ES6/React Sharepoint deployment pipeline.
  • Result:

Setting up a React/ES6 Pipeline

 

class Counter extends React.Component {
  state = {
    count: 0
  }

  handleIncrement = () => {
    this.setState((prevState, props) => {
      return ({ count: prevState.count + 1 });
    })
  }

  handleDecrement = () => {
    this.setState((prevState, props) => {
      return ({ count: prevState.count - 1 });
    })
  }

  render() {
    return (
      <div className='counter-app'>
        <div className='counter'> {this.state.count} </div>
        <button className='counter button' type='button' onClick={this.handleIncrement}>
          increment
        </button>
        <button className='counter button' type='button' onClick={this.handleDecrement}>
          decrement
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById("root")
);

Setting up a React/ES6 Pipeline

 

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) {if (window.CP.shouldStopExecution(1)){break;} var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); }
window.CP.exitedLoop(1);
 } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Counter = function (_React$Component) {
  _inherits(Counter, _React$Component);

  function Counter() {
    var _ref;

    var _temp, _this, _ret;

    _classCallCheck(this, Counter);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {if (window.CP.shouldStopExecution(2)){break;}
      args[_key] = arguments[_key];
    }
window.CP.exitedLoop(2);


    return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Counter.__proto__ || Object.getPrototypeOf(Counter)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
      count: 0
    }, _this.handleIncrement = function () {
      _this.setState(function (prevState, props) {
        return { count: prevState.count + 1 };
      });
    }, _this.handleDecrement = function () {
      _this.setState(function (prevState, props) {
        return { count: prevState.count - 1 };
      });
    }, _temp), _possibleConstructorReturn(_this, _ret);
  }

  _createClass(Counter, [{
    key: 'render',
    value: function render() {
      return React.createElement(
        'div',
        { className: 'counter-app' },
        React.createElement(
          'div',
          { className: 'counter' },
          ' ',
          this.state.count,
          ' '
        ),
        React.createElement(
          'button',
          { className: 'counter button', type: 'button', onClick: this.handleIncrement },
          'increment'
        ),
        React.createElement(
          'button',
          { className: 'counter button', type: 'button', onClick: this.handleDecrement },
          'decrement'
        )
      );
    }
  }]);

  return Counter;
}(React.Component);

ReactDOM.render(React.createElement(Counter, null), document.getElementById("root"));

Source Code!

 

 

Transpile!

 

 

Package!

 

 

Deploy!

Writing your own Pipeline

  • Familiarizing & learning the transpilation toolchain
  • Setting up your own development server
  • Configuring your own local development environment (linting/test-runner/etc.)

Writing your own Pipeline

Worth it if:

  • You like customizing your own tooling or want to use features not directly available in a pre-built pipeline (SASS/LESS, etc)
  • Your widgets need to be in SharePoint to be worked on (require access to multiple data sources in SharePoint or interact heavily with the SharePoint UI)
  • You want the bare minimum: transpilation and deployment.

Using CRA as a Pipeline

Using CRA as a Pipeline

Issues: Very Abstracted System

  • No access to the build tooling without 'ejecting'
  • Not immediately obvious how to extend overall pipeline for own or custom use.
  • Requires a bit of custom finagling to play nice as part of a CMS

Using CRA as a Pipeline

Providing Externals:

Using CRA as a Pipeline

Opinionated Build Additions:

Using CRA as a Pipeline

Opinionated Build Additions:

Using CRA as a Pipeline

Opinionated Build Additions:

  • Do you need code-splitting, or service workers?
  • Do you need automatic source maps, asset manifest generation, etc?
  • Might be overkill, depending on your needs. SharePoint doesn't necessarily aspire to be a PWA.

Using CRA as a Pipeline

Optimized for a single page application:

Automating Deployment

Bless this guy

Authenticating to SharePoint

Do you do weird stuff like Forms Authentication? This guy has got your back!

Saving files to SharePoint

The Holy Grail - Automation!

Using Gulp

Using Gulp

Insert automated deployment into your workflow anywhere:

Using Gulp

Insert automated deployment into your workflow anywhere:

Demo

Caveats and Workarounds

Web Part Properties

  • Not customizable
  • Requires a workaround to provide customization to a widget or web part
  • Global Variables? Configuration JSON? 
  • It gets weird.

Sharing State Between Components

  • Singleton, Global Redux Store
  • Providing alternative Javascript state objects from the Master Page or Page Layout
  • Exposing react lifecycle methods as a global function.

Still getting weird.

Injecting React into existing Markup

The 'Useful' Zone

Injecting React into existing Markup

  • jQuery/Javascript
  • Adding the root divs for react components to the server-side markup
  • Yup, still weird.

SharePoint Feature Pack 2

SPFx

SharePoint Framework ...x

  • Direct Javascript embedding
  • Framework agnostic
  • Modern Toolchain
  • End user configurable

Development Environment

  • Node based
  • Yeoman for scaffolding
    • Provides access to various javascript frameworks currently supported: React/Angular/Knockout(?)/etc.
  • Uses gulp as a task runner and for workflows

Opinionated

  • TypeScript
  • Config is abstracted away
  • Tightly coupled to SharePoint processes

Still nice!

  • Hot Reloading Dev Server!
  • Generator works nicely!
  • Debugging very pleasant!
  • Packages with no problems!

Building a Web Part

Tight Integration

  • Direct access to the Properties Pane!
  • Easy interaction with SharePoint REST layer
  • Easy interaction with traditional C# web parts

Deploying to SharePoint

SharePoint Online

  • Works out of the box, more or less.

SharePoint On Premises

  • Feature Pack 2
  • Configuring an environment for Apps
  • Wildcard SSL certs
  • Subscription Settings
  • Application Domains?
  • Multi Tenant Settings?
  • App Catalogs?

Seriously the config takes forever

Eventually you have an App!

(Brief) Demo

Conclusion

SPFx

Good When

  • You have client side applications that will see a lot of repeated re-use. (List Displays, Carousels, Interactive List based widgets, doodads, etc.)
  • You absolutely need to provide hooks into the web part properties pane.
  • You want close integration with other modern SharePoint features.
  • You can use it at all - (Requires SP2016 w/ FP2 and a lot of config)

DIY React

Good When

  • You absolutely have to set up your own tooling, development environment and practices.
  • You want to escape the boundaries of the standard SharePoint grid and provide your own React Based UI Theming and data layer.
  • Industrial strength client side applications and widgets that live in SharePoint as a hosting solution but don't necessarily interact with it.
  • You don't need to rely as much on a traditional C#/SharePoint base.

Thanks!

Basic Overview of My Stuff

  •  My Shell is 'fish'
  • VS Code with 'Material Palenight Theme'
  • Yarn Package Manager

Integrating ReactJS into SharePoint

By Linus Falck-Ytter

Integrating ReactJS into SharePoint

  • 895