Build multi-page SharePoint Framework web parts with React
When building SharePoint Framework web parts you might need to implement multiple views. Here is how you could do it in React.
Multi-view web parts
In a recent article I discussed the need of implementing multiple views in web parts and how you could switch between them based on user interaction. As an example I used a poll where initially the web part prompts you to configure it. Once that’s done, the web part shows the poll and when the user casts her vote, the overall results are displayed.
Sometimes, in this context the view displayed in the web part is referred to as a page because there is more to it than only its display layer (view). Each view is driven by a controller responsible for loading the data and managing the state of that particular view.
To simplify the code and its maintenance, the different views are build separately. This also makes it easier for a team of developers to work on the same web part simultaneously with fewer conflicts along the way.
Multiple views using UI routing
In the article I wrote recently, I took Angular as an example and showed how you could implement multiple views using routing. As there are some drawbacks using routing with web parts, I showed an alternative using UI routing where the state of the web part isn’t persisted in the URL and doesn’t collide with the state of other elements on the page.
The UI router isn’t specific to Angular and you could use it in React as well. If you however split the different views into separate React components, there is an easier way for you to switch between the different views.
Implementing multiple views in SharePoint Framework web parts built using React
One of the key concepts, when building solutions using React, is splitting the different building blocks into components. Components in React encapsulate functionality and are capable of accepting properties and persisting state. Using properties, components can pass state information between each other and this is exactly how you can control which view of the web part should be displayed at the given time.
Recently, Microsoft published on GitHub a sample SharePoint Framework client-side web part using multiple views built using React. Vesa Juvonen and I discussed the sample in a PnP webcast. Here are the key points that you should remember when building multi-view React web parts yourself.
Components as views
Each view that you implement in your web part is represented by a component. In your web part you would have a main component, that’s bound to the web part and which serves as a bridge between the web part and your React application. This component is also responsible for controlling which view (component) is currently visible. In practice this will depend on a number of factors such as the value of web part properties or user interaction.
Conditionally showing components in React is straightforward and can be done using the following construct:
export class Main extends React.Component<IMainProps, {}> {
// ...
public render(): JSX.Element {
const { needsConfiguration, pollTitle, pollDescription, configureWebPart } = this.props;
return (
<div className={styles.poll}>
{ needsConfiguration &&
<Config configure={configureWebPart} {...this.props} />
}
{ needsConfiguration === false &&
<Poll title={pollTitle} description={pollDescription} {...this.props} />
}
</div>
);
}
}
The value of the needsConfiguration variable is passed from the web part into the main component through its properties. Then, using a logical AND-operator (&&), it’s used to conditionally render the different components that represent the different views.
Read more about conditional rendering with logical &&-operator in the React documentation.
Switching between the views triggered by the web part
The sample web part has two main states: it either prompts the user to configure it, or, when configured, shows the poll. These states are determined by the web part configuration. When the web part is added to the page, it doesn’t have the source list, where the poll questions are stored, configured and prompts the user to configure it. When the poll list is specified in the web part properties, the web part shows the poll.
Switching between the views based on changes in the web part properties is easy. Every change to web part properties triggers the web part’s render method. This is where the main React component is created and where values of the different web part properties can be passed into it, allowing it to render the correct view for the current state. Depending on your scenario, the current state can be determined either by a specific property or by a computed value, such as the needsConfiguration method in the example.
export default class PollWebPart extends BaseClientSideWebPart<IPollWebPartProps> {
// ...
public render(): void {
const element: React.ReactElement<IMainProps> = React.createElement(Main, {
listName: this.properties.listName,
pollTitle: this.properties.pollTitle,
pollDescription: this.properties.pollDescription,
needsConfiguration: this.needsConfiguration(),
displayMode: this.displayMode,
configureWebPart: this.configureWebPart,
pollService: this.pollService
});
ReactDom.render(element, this.domElement);
}
// ...
private needsConfiguration(): boolean {
return this.properties.listName === null ||
this.properties.listName.trim().length === 0 ||
this.properties.pollTitle === null ||
this.properties.pollTitle.trim().length === 0;
}
// ...
}
Switching between the views triggered by components
Web parts that have multiple views, often switch between them based on user interaction. The sample poll web part for example, shows the list of questions, and once the user submitted her vote, the web part switches to another view showing the poll results.
In the example published by Microsoft, switching between the different views triggered by components is controlled by the parent component. Both the poll questions (Vote) and the poll results (Results), are child components referenced in the Poll component.
The vote component exposes through its properties an event called onVoted.
export interface IVoteProps {
onVoted: () => void;
listName: string;
pollService: IPollService;
}
This event occurs after the user submitted her vote.
export class Vote extends React.Component<IVoteProps, IVoteState> {
// ...
private vote(): void {
// ...
this.props.pollService.vote(this.state.voteOptionId, this.props.listName)
.then((): void => {
this.setState((prevState: IVoteState, props: IVoteProps): IVoteState => {
prevState.voting = false;
return prevState;
});
this.props.onVoted();
}, (error: any): void => {
// ...
});
}
}
The parent Poll component implements an event handler that sets the Poll component’s state to show the poll results:
export class Poll extends React.Component<IPollProps, IPollState> {
constructor(props: IPollProps) {
// ...
this.voted = this.voted.bind(this);
}
// ...
public render(): JSX.Element {
const { title, description } = this.props;
const showResults: boolean = this.state.showResults;
return (
<div>
<div className={ 'ms-font-xl' }>{title}</div>
<div className={ 'ms-font-m-plus' }>{description}</div>
<br />
{ showResults === false &&
<Vote onVoted={this.voted} {...this.props} />
}
{ showResults &&
<Results {...this.props} />
}
</div>
);
}
private voted(): void {
this.setState({
showResults: true
});
}
}
The Poll component uses the same conditional rendering as the main component to show the poll questions or the results based on the value of the showResults property.
Nested views
When building web parts with multiple views, in some cases it might be beneficial to nest views and have them share the user interface or behavior. In the sample published by Microsoft, the poll web part uses the Poll component both when showing the poll questions and results. In both cases users should see the title and the description of the poll and it’s more efficient to reuse it across these two views instead of copying it.
export class Poll extends React.Component<IPollProps, IPollState> {
// ...
public render(): JSX.Element {
const { title, description } = this.props;
const showResults: boolean = this.state.showResults;
return (
<div>
<div className={ 'ms-font-xl' }>{title}</div>
<div className={ 'ms-font-m-plus' }>{description}</div>
<br />
{ showResults === false &&
<Vote onVoted={this.voted} {...this.props} />
}
{ showResults &&
<Results {...this.props} />
}
</div>
);
}
// ...
}
Depending on the current state, the Poll component will display one of its children components. Additional benefit of using nested views is that it helps you isolate the code related to a specific portion of your web part. In this case the logic required to switch between the poll questions and poll results is contained within the Poll component and not exposed to the web part itself. Theoretically you could reuse this set of poll components in other web parts as-is without any modification.
Summary
When building SharePoint Framework web parts you might need to implement multiple views in your web part. When switching between the different views it is essential that you don’t modify the URL in the browser’s address bar which could render undesired behavior in other components present on the page. When building web parts using React, one way to implement multiple views in a web part is by conditionally showing the different components that represent the different views. Recently Microsoft published a code sample illustrating how to do this. The code sample is available on GitHub at https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-multipage.