Open rmccue opened 6 years ago
The code I'm using currently:
export const pages = new handler( {
nonce: API_NONCE,
type: 'page',
url: `${ API_ROOT }wp/v2/pages`,
query: {
_embed: '1',
},
} );
// Add our extra tools.
export const normalizePath = path => path.replace( /^\/+|\/+$/g, '' );
const pathForPage = page => normalizePath( page.link.substr( SITE_HOME.length ) );
pages.archiveForPath = path => `pages/${ normalizePath( path ) }`;
pages.fetchPageByPath = path => {
// Query by slug for the final path component.
const normalized = normalizePath( path );
const components = normalized.split( '/' );
const id = pages.archiveForPath( path );
pages.registerArchive( id, {
slug: components.slice( -1 )[0],
} );
return pages.fetchArchive( id );
};
pages.getPageByPath = ( state, path ) => {
const normalized = normalizePath( path );
const allMatching = pages.getArchive( state.pages, pages.archiveForPath( normalized ) );
if ( ! allMatching ) {
return null;
}
// Whittle down to the only one that matches fully.
return allMatching.find( page => pathForPage( page ) === normalized );
};
Would you be able to produce an extended example of how one can implement the dynamic-archive approach to slug-based routing? In this case, I'm not sure how to approach wiring the repress handler with react-router and the rendering page component. Many thanks for the cool library!
Nevermind, I think I figured it out! I was attempting to connect the rendering component with the redux state via the withArchive() HOC, but I'm guessing it's not designed to be extended to handle dispatching your custom action creator, pages.getPageByPath. In case it's helpful for anyone else:
Page.js:
import { connect } from 'react-redux';
import React, { Component } from 'react';
// rmccue's code above: https://github.com/humanmade/repress/issues/3#issuecomment-373575556
// renaming handler to disambiguate from redux state "pages"
import { pages as pagesHandler } from '../reducers/wp_types';
class Page extends Component {
constructor(props){
super(props);
const page = pagesHandler.getPageByPath(props, props.location.pathname);
if (!page){
props.fetchPageByPath(props.location.pathname);
}
this.state = { page };
}
static getDerivedStateFromProps(nextProps, prevState){
return { page: pagesHandler.getPageByPath(nextProps, nextProps.location.pathname) };
}
render(){
const { page } = this.state;
return (
<div className="Page">
{ page ? <h1 dangerouslySetInnerHTML={ { __html: page.title.rendered } } /> : ''}
</div>
)
}
}
const mapStateToProps = ({ pages }) => ({
pages // redux store pages
});
const mapDispatchToProps = (dispatch) => ({
fetchPageByPath: (path) => { dispatch(pagesHandler.fetchPageByPath(path)) }
});
export default connect(mapStateToProps, mapDispatchToProps)(Page);
index.js (router detail):
const AppContainer = () => (
<ConnectedSwitch>
<Route exact path="/" component={HomePosts}/>
<Route path="/post/:id" component={Post} />
<Route path="/:slug" component={Page} />
</ConnectedSwitch>
);
Yeah, the way to use it with withArchive
is to build it into your component a bit more; pseudo-code is something like:
const ConnectedComponent = props => {
const { loading, path, posts } = props;
if ( loading ) {
return <p>Loading</p>;
}
if ( ! posts ) {
return <p>404</p>;
}
const matched = posts.find( post => post.url === SITE_BASE + path );
if ( ! matched ) {
return <p>Also 404</p>;
}
return <Page data={ matched } />;
};
export default withArchive(
pages,
state => state.pages,
props => {
const id = `_page/${ props.path }`;
posts.registerArchive( id, {
slug: props.path.split( '/' ).slice( -1 )[0],
} );
return id;
}
);
I find myself having to repeat the dynamic-archive style for things like pages all the time. We should build this in.