I'm trying to bump React from 0.13.3 to 0.14.8, but my old setup doesn't work anymore. I've solved all issues very easily, but now I'm stuck and I can't find any hint in the upgrad guide(s).
Here is my setup:
const createElement = (Component, props) => <Component {...props} {...store.getState()}/>
const routes = <Router createElement{createElement} history={browserHistory}><Route>...</Route></Router>;
store.subscribe(() => ReactDOM.render(routes, containerElement));
So basically, everytime the store changes, ReactDOM.render is called and routes is rendered. routes should call createElement on render and did just that in 0.13, but not anymore in 0.14. Only when the route/url changes, the UI is updated with the latest data.
Any idea where the problem is coming from (React, ReactRouter, Redux) and how to debug/fix are most welcome!
Related
I am building a React app with sagas for Redux and next.js. The main app.js file is handled as follows:
import { wrapper } from '../state/store';
const App = ({ Component, pageProps }) => (
<Layout>
<Component {...pageProps} />
</Layout>
);
export default wrapper.withRedux(App);
I can call and write to the state using dispatch for the pages in this app and some of their direct children. However, with others I get an error that is always something like ...
Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
I don't understand how the parent page and some children are fine, but others have problems. It seems like components must be registered somewhere to be in the provider as every new component that I add has problems.
If the app is not wrapped in a provider, why does it work in the main page and some children? If it is wrapped, why aren't some components seeing that?
I'm new to react and redux, I started out my project with the react-router-dom, now I'm looking to add Redux in the mix.
Should I keep using react-router-dom or just install react-router-redux? or both?
Does the Route component work the same? I'm a bit confused as what the use of react-router-dom would be?
Do I switch all the Link components to an a element that dispatches an action to the store?
Thanks for any help.
Generally when using Redux, it's recommended to use react-router-redux, which encapsulates react-router.
Only when you initialize your app, you should pass a couple of things from react-router to Redux, which are Router and browserHistory.
In app.js:
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
const initialState = {};
const store = configureStore(initialState, browserHistory);
const history = syncHistoryWithStore(browserHistory, store);
const render = () => {
ReactDOM.render(
<Provider store={store}>
<Router
history={history}
routes={rootRoute}
/>
</Provider>,
document.getElementById('app')
);
};
You provide the Router element of react as a child of Redux's Provider element, and you also provide to the Router element the Redux wrapper for React's browserHistory.
In the rest of your project, you only need to use react-router-redux, rather than calling React directly.
react-router-dom BTW is just another layer of simplification in React 4 which encapsulates react-router, but in case your project architecture is Redux, than you need to work according to the rules of Redux and therefore only use react-router-redux in order to pass through the store in each action (except for the aforementioned initialization).
i will recommend you to use react-router-redux.
using react-router-redux you can connect your router state with your Redux Store so that you can Interact with the Router with the same API you use to interact with the rest of your app state.
Use react-router-redux.
With react-router-redux the location state is kept in your redux store as plain object. This means you can inject location information with connect as you would any other state.
Cannot seem to find much quality documentation on this. React Router v4 documentation has information of the 'history' object but states that it is mutable and should not be changed. Many examples I have seen seem like 'hacks'. Any insight would be appreciated.
I had to grapple with this one for a while as a react newbie myself. While the link that #ToddChaffee provided gives you a good insight, this thread highlights that the history object is automatically passed as a prop when you have <Router/> as the top level component. That makes it much simpler than loading the history object separately and using the createBrowserHistory() method.
The following worked for me:
<Route exact path='/' render={({history}) =>
<Home infoHandler={this.handleUserInfo}
imageHandler={this.handleImage}
history={history}/>
}/>
Then inside the component you can:
this.props.history.push('/routeYouWantToGoTo');
Don't forget to typecheck the prop in you component before you export the module:
Home.propTypes = {
history: React.PropTypes.object
}
export default Home;
I did upgrade from react 0.13 to the newest one. Here a list of the dependencies I changed.
react: 0.13.3 -> 0.14.3
react-dom: none -> 0.14.3
react-router: 0.13.3 -> 1.0.3
react-history: none -> 1.0.3
react-intl: 1.2.0 -> 1.2.2
intl: 1.0.0 -> 1.0.1
But I cant get the translations work again.
Uncaught ReferenceError: Could not find Intl message: test
// app.js
var React = require('react');
var ReactDom = require('react-dom');
var Router = require('react-router').Router;
var routes = require('./routes');
var createHistory = require('history').createHistory;
var intlData = {
locales: 'en',
messages: {
test: 'Test'
}
};
ReactDom.render(<Router history={createHistory()} intl={intlData}>{routes}</Router>, document.getElementById('app'));
// Test.js
var React = require('react');
var ReactIntl = require('react-intl');
var IntlMixin = ReactIntl.IntlMixin;
var Test = React.createClass({
mixins: [IntlMixin],
render: function() {
return (
<div>
<h1>
{this.getIntlMessage('test')}
</h1>
</div>
);
}
});
module.exports = Test;
What did I miss in the update? What is the correct way to init my messages.
IntlMixin works by looking on the context for the relevant data, specifically, this.context.locales, this.context.formats, and this.context.messages. It was probably the React Router upgrade from 0.x to 1.x — which changed the way the top-level component is handled — that broke things.
However, if you look at the source linked above, you can see that the mixin looks for the data on this.context only if it's not found on this.props. Thus, if you could pass your internationalization data to every route handler as a prop, things would work fine. Luckily, React Router allows you to do just that via createElement.
createElement should be a function that takes two arguments: the component React Router wants to render, and the props it wants to send it. The function should render that component with the given props. The default implementation looks like this:
function createElement(Component, props) {
return <Component {...props} />
}
However, you can further customize how the component gets rendered. For your case, it might look something like:
function createIntlElement(Component, props) {
return <Component intl={intlData} {...props} />
}
although it looks like from the React Intl docs that the props should be split out, so if that doesn't work, maybe more like:
function createIntlElement(Component, props) {
return <Component {...props}
locales={intlData.locales}
messages={intlData.messages} />
}
or, more simply using the spread syntax,
function createIntlElement(Component, props) {
return <Component {...props} {...intlData} />
}
You then instruct the router to use your createElement function by passing it to the createElement prop:
var router = (
<Router history={createHistory()} createElement={createIntlElement}>
{routes}
</Router>
);
ReactDom.render(router, document.getElementById('app'));
Don't forget to include IntlMixin on all your route handlers so that child components that aren't directly rendered by the router still have access to the context.
React Intl version 2 (now in beta) appears to use a IntlProvider component, much like Redux's Provider component, to provide the correct context for you, so using createElement should not be necessary when using that version.
http://fluxxor.com/examples/react-router.html#/
This shows how to do it for the old version of React-router.
Does anyone know how to pass flux as a prop to your top-level component in react-router 1.0.0?
I saw somewhere in the docs that in react-router 1.0.0 routes can just be described as objects and this means you can pass in arbitary props. However this isn't working for me:
routes =
path: "/"
flux: flux
component: App
childRoutes: [
{path: "/path/to/path", component: Comp, flux: flux}
]
React.render(<Router routes={routes} />, document.getElementById("app"))
No sign of flux as a prop on App.
The syntax is Coffee Script but basically routes is an object,
As stated here:
https://github.com/BinaryMuse/fluxxor/issues/137
You can pass a function to the Router createElement property which passes the flux property down to the children components.
const createFluxComponent = (Component, props) => {
return <Component {...props} flux={flux} />;
};
React.render(
<Router createElement={createFluxComponent}>
...