Reputation: 1843
I put it on C9 because there's a lot of dependencies and it's probably easier to read the code there than it is to showcase it all here.
https://ide.c9.io/noman2000/showfer-media
See MainWrapper.jsx
, LibraryView.jsx
, Library.jsx
, and connect.js
.
The app itself runs here:
https://showfer-media-noman2000.c9users.io/
The nextjs team has an example app here:
https://github.com/zeit/next.js/tree/master/examples/with-redux
But I need to convert my app, which is a bit different. The error it gives me when I do this is:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
in Unknown (created by Container)
in AppContainer (created by Container)
in Container (created by App)
in App
The important bits. I have a route which displays this component Library.jsx
.
// @flow
import React from "react"
import PropTypes from "prop-types"
import routes from "./../../../server/routes/routes"
import commonApi from "./../../../app/common/common"
import LibraryItem from "./../../../app/components/library/LibraryItem"
import detectEnvironment from "./../../../app/common/detect-environment"
const { categoryContent } = commonApi
const { isMobileFromWidth } = detectEnvironment
const { Router } = routes
class Library extends React.Component<Props> {
componentWillMount() {
this.props.actions.setActiveCategory(null, "", -1)
}
authLibraryItems = ["favorites", "history", "queue"]
advtLabel = (label: string) => {
const { library } = this.props.state.toJS()
const sourceUrl = library.serviceTypes[label].source_url
const slashOrder = sourceUrl.substr(8).search("/") + 1
return sourceUrl.substr(8).slice(slashOrder)
}
render() {
const { auth, library, layout } = this.props.state.toJS()
const titles = library.serviceTypes.heading
const categoryClasses = isMobileFromWidth()
? categoryContent(layout.orientation)
: "category-content"
return (
<React.StrictMode>
<section>
<div className={categoryClasses}>
<div className="library-title-container">
<div className="library-title">{titles && titles.title}</div>
<div className="library-subtitle">{titles && titles.subtitle}</div>
</div>
<div className="library-menu scrolling-wrapper">
<h1>I can display!</h1>
</div>
</div>
</section>
</React.StrictMode>
)
}
}
export default Library
That component is wrapped up in a Page LibraryView.jsx
.
import connectWrapper from "./../app/redux/utils/connect"
import root from "./../app/redux/rootReducer"
import Library from './../app/components/main/Library'
import MainWrapper from './../app/components/main/MainWrapper'
const { rootActions } = root
export default MainWrapper(connectWrapper(rootActions, Library))
That MainWrapper.jsx
looks like this:
// @flow
/* eslint-disable */
import root from "window-or-global"
import React from "react"
import { fromJS } from "immutable"
import { connect, Provider } from "react-redux"
import "babel-polyfill"
import storeConfiguration from "../../redux/configureStore"
import rootReducer from "../../redux/rootReducer"
import commonApi from "./../../common/common"
const { getOrCreateStore } = storeConfiguration
export default (...args) => (Component) => {
// First argument is initStore, the rest are redux connect arguments and get passed
const [initStore, ...connectArgs] = args
console.log(Component)
const ComponentWithRedux = (props = {}) => {
const { store, initialProps, initialState } = props
// Connect page to redux with connect arguments
const ConnectedComponent = connect.apply(null, connectArgs)(Component)
// Wrap with redux Provider with store
// Create connected page with initialProps
return React.createElement(
Provider,
{ store: store && store.dispatch ? store : getOrCreateStore(initStore, initialState) },
React.createElement(ConnectedComponent, initialProps)
)
}
ComponentWithRedux.getInitialProps = async (props = {}) => {
const isServer = checkServer()
const store = getOrCreateStore(initStore)
// Run page getInitialProps with store and isServer
const initialProps = Component.getInitialProps
? await Component.getInitialProps({ ...props, isServer, store })
: {}
return {
store,
initialState: store.getState(),
initialProps
}
}
return ComponentWithRedux
}
And finally, the connect.js
file.
// @flow
import { connect } from "react-redux"
import { bindActionCreators } from "redux"
import immutable from "immutable"
const connectWrapper = (actions: {}, view: any) => {
const mapStateToProps = state => ({ state })
const mapDispatchToProps = (dispatch: Function) => ({
actions: bindActionCreators(actions, dispatch),
})
return connect(mapStateToProps, mapDispatchToProps)(view)
}
export default connectWrapper
What I would like to do is to be able to pass any component into MainWrapper(connectWrapper(rootActions, <ANY REACT COMPONENT>))
and have it wrapped in a redux store, but that isn't working.
This is partially resolved by MainWrapper()(connectWrapper(rootActions, <Any React Component>))
but it's very wonky.
Upvotes: 1
Views: 2773
Reputation: 1672
If I understood you correctly, you are trying to build your own redux-wrapper HOC for next.js. I would suggest using the next-redux-wrapper library which can be found here together with a great example.
You than simply have to wrap all your page components with the withRedux HOC provided by next-redux-wrapper and all child components can be wrapped in the connect function provided by react-redux, as they state in the docs:
Use withRedux to wrap only top level pages! All other components should keep using regular connect function of React Redux.
Upvotes: 3