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 & PackageClient side & server sideDowntimeSite-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
Integrating ReactJS into SharePoint
By Linus Falck-Ytter
Integrating ReactJS into SharePoint
- 1,010