This is the way I configure my client to render the proper language through react-intl.
import localeData from './translations/en.json';
//import localeData from './translations/xx.json'; <-- any language
const render = routes => {
match({ history, routes }, (error, redirectLocation, renderProps) => {
ReactDOM.render(
<HotEnabler>
<IntlProvider locale={locale} messages={localeData}>
<Provider store={store} app={app} restApp={restApp} key="provider">
<Router {...renderProps} render={renderRouter} history={history}>
{routes}
</Router>
</Provider>
</IntlProvider>
</HotEnabler>,
dest
);
});
};
render(getRoutes(store));
However I would like to import the localeData dynamically based on the locale within a cookie. So if the locale of my user is "en", I will only load in the en.json file.
const locale = Cookie.get('locale') || 'en';
const render = routes => {
match({ history, routes }, (error, redirectLocation, renderProps) => {
ReactDOM.render(
<HotEnabler>
<IntlProvider locale={locale} messages={localeData}>
<Provider store={store} app={app} restApp={restApp} key="provider">
<Router {...renderProps} render={renderRouter} history={history}>
{routes}
</Router>
</Provider>
</IntlProvider>
</HotEnabler>,
dest
);
});
};
render(getRoutes(store));
What would be the proper way of doing this? Tried creating a function but I can't pass the data properly to messages.
Thanks
Got it solved through the following codes. Post them here in case someone needs it.
const languages = {
en: require('./translations/en.json'),
zn: require('./translations/zn.json')
};
const localeData = languages[locale];
Related
I'm trying to load a static HTML from the meteor server side that also uses redux and router. However, every time I try to render my component with renderToString() I get the error indicated in the title. What am I doing wrong?
I've already tried using React.renderComponentToString as a possible alternative, but I get the error that React.renderComponentToString is not a function. How else am I going to pass an argument?
import React from 'react';
import {onPageLoad} from 'meteor/server-render';
import {StaticRouter} from 'react-router';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import {Helmet} from 'react-helmet';
import rootReducer, {initialState} from '../redux/reducers';
import HomePage from '../ui/homepage/HomePage';
export default renderServerPage = onPageLoad(sink => {
const context = {};
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
const MyApp = props => {
return (
<Provider store={store}>
<StaticRouter location={sink.request.url} context={context}>
<Route path="/" component={HomePage}/>
</StaticRouter>
</Provider>
);
}
// The following line is causing the error
sink.renderIntoElementById('app', renderToString(<MyApp />));
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.title.toString(
"Afterschools, Enrichment Programs, Growth, & Experiences - Fun2Bright"
));
sink.appendToBody(
<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
</script>
);
});
My other attempts include the following:
1) creating a component outside of the scope within the same file and passing store and sink.request.url as props
function MyApp (props) {
const context = {};
return (
<Provider store={props.store}>
<StaticRouter location={props.location} context={context}>
<Route path="/" component={HomePage}/>
</StaticRouter>
</Provider>
);
}
2) basically creating the same component but in another file and importing the component.
3) directly putting the element inside the renderToString:
sink.renderIntoElementById('app', renderToString(
<Provider store={store}>
<StaticRouter location={sink.request.url} context={context}>
<Route path="/" component={HomePage}/>
</StaticRouter>
</Provider>
));
4) using React.createElement without passing any props:
sink.renderIntoElementById('app', renderToString(React.createElement(MyApp)));
5) commenting out the following just to see if it's caused by any of my other imported components
<Route path="/" component={HomePage}/>
I finally figured the issue. It turns out that simply using react-router doesn't allow me to use DOM-aware components like and , so I needed to use react-router-dom instead. react-router-dom also re-exports all of react-router's exports, so I'll only ever need to use react-router-dom. I changed everything to be imported from 'react-router-dom':
import {Route, StaticRouter} from 'react-router-dom';
...
// inside onPageLoad
const App = props => {
return (
<Provider store={store}>
<StaticRouter location={props.location} context={context}>
<Route path="/" component={HomePage}/>
</StaticRouter>
</Provider>
);
}
sink.renderIntoElementById('app', renderToString(<App store={store} location={sink.request.url}/>));
I found this from here: https://github.com/ReactTraining/react-router/issues/4648
I'm using connected-react-router with my react redux app.
I need server side rendering and client side rendering (I'm using a react component in a symfony twig template via limenius react bundle).
My probleme is that i cannot use basename properly. I have a locale isocode in my URL (www.localhost.com/fr/mypage)
If i declare a basename '/fr/' in history:
<Route exact path={`/${isocode}/checkout/customize`} component={Customize} />
works !
... but I want this :
<Route exact path="checkout/customize" component={Customize} />
and it does not work !
What i have in my app.js:
export const App = () => {
const store = ReactOnRails.getStore('CustomizeStore');
const state = store.getState();
const { isocode } = state.general.data.locale;
const history = createHistory({
basename: `/${isocode}/`,
initialEntries: [state.router.location.pathname],
});
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route exact path={`/${isocode}/checkout/customize`} component={Customize} />
</Switch>
</ConnectedRouter>
</Provider>
);
};
and in my store.js
export const createHistory = historyConfig =>
isServer ? createMemoryHistory(historyConfig) : createBrowserHistory();
export default (props, context) => {
const { baseURL } = props.general.data.api;
const { isocode } = props.general.data.locale;
const history = createHistory({ basename: `/${isocode}/`, initialEntries: [context.pathname] });
return createStore(
reducers(history),
{ ...props },
composeEnhancers(
applyMiddleware(thunk, routerMiddleware(history)),
),
);
};
What i expect in my app.js:
<Provider store={store}>
<ConnectedRouter history={history}>
<Switch>
<Route exact path="checkout/customize" component={Customize} />
</Switch>
</ConnectedRouter>
</Provider>
I've read that the basename for "browserRouter" and "staticRouter" provided by react-router should be declared in history props of ConnectRouter component.
What I'm doing wrong ? Is connect-react-router a good choice for my redux ssr application or should i use react-router ? (I'm using immutable.js and i want to implement hot-reloading if possible =)
A big thanks !
I faced the same problem a few days ago and I solved it by setting the basename in the createBrowserHistory, like this:
const history = createBrowserHistory({ basename: “/baseName” })
I try to get resultsState by call react-instantsearch's findResultsState API, But can't integrate with the component already connected with redux store. Error will show like below.
Invariant Violation: Could not find "store" in either the context or props of component . Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to component
Call api like this
findResultsState(
component: Search, {
searchState
});
My component looks like this. It is composed with some HOCs, and some HOC will connect with redux store through react-redux connect function.
Search = compose(
intlWrapper,
withModal,
withGlobalAlert,
withMainLayout,
injectIntl,
)(Search);
Server-side Provider will render like this
const appString = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={ context }>
<Main />
</StaticRouter>
</Provider>
);
Client-side:
const store = configureStore(history,
window.__APP_INITIAL_REDUX_STATE__);
// Render component
hydrate(
<Provider store={store}>
<ConnectedRouter history={history}>
<Main />
</ConnectedRouter>
</Provider>,
document.getElementById('root'),
);
Main
<Switch>
{ routes.map( route => <Route key={ route.path } { ...route } />)}
</Switch>
routes
export default [
{
path: '/search',
component: Search,
},{
...
}
...
];
I did find out that I can't use when I'm rendering my app on the server. I would like to wrap my App in specified router depending on the situation - BrowserRouter on the CLient side and StaticRouter on server side. My App looks like this:
imports......
class App extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<BrowserRouter>
<div>
<Menu />
<main>
<Switch>
<Route exact path="/about" component = {About} />
<Route exact path="/admin" component = {BooksForm} />
<Route exact path="/cart" component = {Cart} />
<Route exact path="/" component = {BookList} />
</Switch>
<Footer />
</main>
</div>
</BrowserRouter>
);
}
componentDidMount(){
this.props.getCart();
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
getCart
}, dispatch)
}
export default connect(null, mapDispatchToProps)(App);
I tried to move my BrowserRouter ou of this component so my index.js would look like this:
imports....
const renderApp = () => (
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
)
const root = document.getElementById('app')
render(renderApp(), root)
So I would get ability to wrap my app in different routers. The problem is when I moved BrowserRouter out of my App component it stopped working. Clicking on links just does not work anymore. The url is changing but my app isn't rendering differnet components. How can I move router out of this component?
On the server, you'll wrap your app similar to this:
const routerContext = {};
const appComponent = (
<Provider store={store}>
<StaticRouter location={req.url} context={routerContext}>
<App />
</StaticRouter>
</Provider>
);
Where you pass react-router the location (from the url) as well as a context object.
The client side is like your example:
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
I'm trying to get react-router to work in an electron app. I'm using a boilerplate and stripped away some of the stuff I don't want but that should provide a pretty solid summary of the technologies being used.
I cannot for the life of my get the router to work as expected. To me this means that when I use history.push('/someroute/') or <Link to="someroute"..., the corresponding component to that router should be rendered and the previous one gone.
Here is my routes file:
export default () => (
<App>
<Switch>
<Route path="/" component={MyDevkitsPage} />
<Route path="/browser/" component={DevkitBrowserPage} />
<Route path="/Console/" component={ConsolePage} />
<Route path="/Controller/:devkitIp" component={ControllerPage} />
<Route path="/Setting/:devkitIp" component={SettingPage} />
</Switch>
</App>
);
I would expect that if <Link to="/browser/"> aaaa </Link> is clicked, it would go to the /browser/ component. Instead, I get an error:
Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack
Likewise, if I move the path="/browser/" line above the path="/" line in routes, it will always go to /browser/.
edit: RootContainer
export default function Root({ store, history }: RootType) {
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>
);
}
Edit 2: Configure store
const configureStore = (initialState?: counterStateType) => {
// Redux Configuration
const middleware = [];
const enhancers = [];
// Thunk Middleware
middleware.push(thunk);
// Logging Middleware
const logger = createLogger({
level: 'info',
collapsed: true
});
middleware.push(logger);
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Redux DevTools Configuration
const actionCreators = {
...counterActions,
...routerActions,
};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
actionCreators,
})
: compose;
/* eslint-enable no-underscore-dangle */
// Apply Middleware & Compose Enhancers
enhancers.push(applyMiddleware(...middleware));
const enhancer = composeEnhancers(...enhancers);
// Create Store
const store = createStore(rootReducer, initialState, enhancer);
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')) // eslint-disable-line global-require
);
}
return store;
};
You will need to change <Route path="/" component={MyDevkitsPage} /> to <Route exact path="/" component={MyDevkitsPage} />.
By adding exact to that <Route /> you will prevent react-router from trying to load both <MyDevkitsPage /> and <DevkitBrowserPage /> at the same time.
I had the same issue. One thing that I notice is that if I place the route path after all the others, it will not throw the exception.
My Code:
export default () => (
<App>
<Switch>
<Route path="/counter" component={CounterPage} />
<Route path="/calculator" component={Calculator} />
<Route path="/" component={HomePage} />
</Switch>
</App>
);
This code worked for me I've changed BrowseRouter to MemoryRouter
import React from 'react'
import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';
import routes from './Route';
function App() {
return (
<>
<Router>
<Routes>
{routes.map((route) => {
return <Route path={route.path} element={route.element} />
})}
</Routes>
</Router>
</>
);
}
export default App;