I am working in a react application using redux, react-router and react-router-redux. So it is a simple App and in this application I need use a simple, route view. I have read the documentation and can made mi route without problems, but at moment of added a Template at routes I had problems. I need pass the dispatched and State of the reducers to the views and the Template for can manipulate spinners and other elements in the Template. I don't sure the how make it or if I am making correctly.
Could you help me, please?
This is my router code:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {Route} from 'react-router';
import {ConnectedRouter} from 'react-router-redux';
import {Switch} from 'react-router-dom';
import {store, history} from './store/index';
import Layout from './components/Layout';
import './bootstrap-3/css/bootstrap.min.css';
/* Components */
import Home from './components/abouts/home';
import About from './components/abouts/about';
import NoMatch from './components/abouts/nomatch';
import Artists from './components/artists';
const NodeId = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Layout>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/artists" component={Artists}/>
<Route component={NoMatch}/>
</Switch>
</Layout>
</ConnectedRouter>
</Provider>, NodeId);
So I don't sure of how pass the redux State and the dispatched to component of route, So I am doing something like that in each component, I thing that there is one better form of do it.
import {connect} from 'react-redux';
import * as artistActions from './actions/artistActions';
import * as globalConfigActions from './actions/globalConfigActions';
class Abouts extends Component {
render() {
console.log('Abouts Component: ', this);
return (
<div>
<h1>About</h1>
</div>
);
}
}
const mapStaeToProps = (state) => {
return {
artist: state.artist,
globalConfig: state.globalConfig
}
};
const mapDispatchToProps = (dispatch) => {
return {
fetchArtist: () => {
return dispatch(artistActions.fetchArtist());
},
globalConfigActions: {
showSpinner: () => {
dispatch(globalConfigActions.showSpinner());
},
hiddenSpinner: () => {
dispatch(globalConfigActions.hiddenSpinner());
}
}
}
};
export default connect (mapStaeToProps, mapDispatchToProps)(About);
Of that form I can pass the State and the dispatched to component to each main component but I need pass the State and the dispatched to the Template, when I have tried do it with the template the routes doesn't work and the view doesn't change.
If I use one simple template without the "mapDispatchToProps" and "mapStaeToProps" of redux it works perfect but the layout doesn't have access to methods or state of redux.
Someone could say me how I can make it correctly, please?
Thank you very much
Related
I do not control the modal components in my company. The I am using redux-toolkit so I am wondering if this is the issue. I have tried a few different iterations trying to get this to work and tried mimicing another app that it works with (standard redux app).
The below code is me trying to mimic the other app that has it working.
my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'proxima-nova/scss/proxima-nova.scss'
import './css/styles.scss';
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import { RcReducer } from './Store/RC/Events';
import { RcDataReducer } from './Store/RC/RcData';
const middleware = [...getDefaultMiddleware()];
export const ReduxStore = configureStore({
reducer: {
RcEvents: RcReducer,
RcData: RcDataReducer,
},
middleware,
});
ReactDOM.render(<App store={ReduxStore} />, document.getElementById('app'));
My app.tsx
import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Wrapper from './Components/Wrapper/Wrapper';
import './App.scss';
import Loader from './Components/Common/Loader/Loader';
import { Provider } from 'react-redux';
import StoreProvider from './Store/Store';
const Home = lazy(() => import('./Components/Pages/Home/Home'));
const RcDashboard = lazy(() => import('./Components/Pages/RC/RC'));
const EccDashboard = lazy(() => import('./Components/Pages/ECC/Dashboard/Dashboard'));
const App: React.FunctionComponent<{ store: any }> = ({ store }: { store: any }) => {
return (
<Provider store={store}>
<StoreProvider>
<Router>
<div id='root'>
<Wrapper>
<Suspense fallback={<Loader overlay={true} fullscreen={false} size='xl' />}>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/rc' component={RcDashboard} />
<Route path='/ecc' exact component={EccDashboard} />
</Switch>
</Suspense>
</Wrapper>
</div>
</Router>
</StoreProvider>
</Provider>
);
};
export default App;
I am calling a component that is provided for use within my company. I personally do not have control over how this component acts. It is a modal. The modal does not just append itself to the component it is called in and automatically appends itself to the body tag.
For some reason the neither my context nor my redux store are showing up. I instead get this.
I am currently at a loss as to how I can get this working. I appreciate any help provided.
I would assume that that modal if somehow forwarding the context value, but probably only legacy context. So it might work with an older version or react-redux, but not with a current one.
You'll have to either use a Portal in the modal instead of creating a completely new ReactDOM instance (which is probably happening right now) or forward the current value to a new in the modal, like this
function YourComponent(){
const store = useStore()
return <Modal><Provider store={store}>whatEverGoesIntoYourModal</Provider></Modal>
}
I have to combine use of Redux and React Router.
I tried react Router alone first and when I was clicking my images I was correctly redirected.
I followed redux tutorial and now when I click my images, I change the address (ex: http://localhost:3000/contact) but nothing displays as if the component was empty.
Root.js
import React from 'react';
import './index.css';
import PropTypes from 'prop-types'
import ReactDOM, { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
import App from './App'
import Users from './users'
import Book from './Book'
import Notfound from './notfound'
import { Provider } from 'react-redux';
import Store from './redux/Store/store'
import * as serviceWorker from './serviceWorker';
const Root = ({ store }) => (
<Provider store = { Store }>
<Router>
<div>
<Switch>
<Route exact path="/:filter?" component={App} />
<Route path="/users" component={Users} />
<Route path="/book" component={Book} />
<Route path='/manual' component={() => { window.location = 'https://------'; return null;} }/>
<Route path='/contact' component={() => { window.location = 'https://-------'; return null;} }/>
<Route component={Notfound} />
</Switch>
</div>
</Router>
</Provider>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
serviceWorker.unregister();
export default Root
index.js:
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import myReducer from './redux/Reducers/myReducer'
import Root from './Root'
const store = createStore(myReducer)
render(<Root store={store} />, document.getElementById('root'))
App.js:
import React from 'react'
import { Route, Link, Redirect, withRouter, BrowserRouter as Router } from 'react-router-dom'
import logo from './images/logo.png';
import book from './images/book.png';
class App extends React.Component {
constructor() {
super();
this.state = {
date: new Date()
};
}
render() {
const { date } = this.state;
return (
<div>
<img src={logo} />
<img src={book} onClick={() => this.props.history.push('/book')}/>
<img src={call} onClick={() => this.props.history.push('/contact')}/>
</div>
)
}
}
export default App
Do you know what is wrong ?
A few things I noticed:
When using react router you shouldn't use window.location to redirect since this reloads the whole page. The <Redirect> component from react-router is a better choice here.
Also you shouldn't use the component prop on the <Route>-component for things that aren't actually components, as there's the render prop for that (more on that here).
Furthermore: <Route exact path="/:filter?" component={App} /> is not going to work since :filter? is looking for a variable and exact is looking for an exact match. Moreover you probably shouldn't put the flexible one first since it's going to match every route that you throw at it. So all the following routes are practically unreachable.
My links in the following code are not working. I'm a beginner and I'm not sure on where the issue is.
Do you have any tips on how to debug that?
Thank you,
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import {fetchItems} from './actions/items';
import { connect } from 'react-redux';
import './App.css';
import Home from './components/Home.js'
import Additem from './components/Additem'
import Mybag from './components/Mybag.js'
import About from './components/About.js'
import ItemShow from './components/ItemShow.js'
import NavigationBar from './components/NavigationBar.js'
class App extends Component {
constructor(props){
super(props)
}
componentDidMount(){
this.props.fetchItems();
}
render() {
console.log("itemList: ", itemList)
const itemList = this.props.items
return (
<div className="App">
<NavigationBar />
<React.Fragment>
<Route exact path='/' render={routerProps => <Home {...routerProps} items={itemList}/>} />
<Route exact path={`/items/:itemID`} component={ItemShow} />
<Route exact path="/my_bag" component={Mybag} />
<Route exact path="/add_item" component={Additem} />
<Route exact path="/about" component={About} />
</React.Fragment>
</div>
)
}
}
const mapStateToProps = state => {
return {
items: state.items
}
}
const mapDispatchToProps = dispatch => {
return {
fetchItems: (items) => dispatch(fetchItems(items)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
I used to have a component in charge of fetching my items in the DB and load them. It was working but refactored to include the fetch as a redux action and since then, it is not working anymore.
Please let me know if you have any tips.
Thank you!
Wrap your connected component with react-router's withRouter function:
// Adding the imports just for clarity.
import { connect } from "react-redux";
import { compose } from "redux";
import { withRouter } from "react-router-dom";
// compose-style
compose(
withRouter,
connect(...)
)(YourComponent);
// Without compose
withRouter(connect(...))(Your component);
For more information: withRouter API
sn42 is right. But I prefer to export the container with withRouter function rather than using compose
export default withRouter((connect(mapStateToProps, mapDispatchToProps)(App)));
I need the match object at the navbar. I am using React, Redux and TypeScript.
Here is my boot-client file:
import './css/site.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
import configureStore from './configureStore';
import { ApplicationState } from './store/initialState';
import * as RoutesModule from './routes';
let routes = RoutesModule.routes;
// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
const history = createBrowserHistory({ basename: baseUrl });
// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = (window as any).initialReduxState as ApplicationState;
const store = configureStore(history, initialState);
function renderApp() {
// This code starts up the React app when it runs in a browser. It sets up the routing configuration
// and injects the app into a DOM element.
ReactDOM.render(
<AppContainer>
<Provider store={ store }>
<ConnectedRouter history={ history } children={ routes } />
</Provider>
</AppContainer>,
document.getElementById('react-app')
);
}
renderApp();
// Allow Hot Module Replacement
if (module.hot) {
module.hot.accept('./routes', () => {
routes = require<typeof RoutesModule>('./routes').routes;
renderApp();
});
}
This is my boot-server file:
import * as React from 'react';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { replace } from 'react-router-redux';
import { createMemoryHistory } from 'history';
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
import { routes } from './routes';
import configureStore from './configureStore';
export default createServerRenderer(params => {
return new Promise<RenderResult>((resolve, reject) => {
// Prepare Redux store with in-memory history, and dispatch a navigation event
// corresponding to the incoming URL
const basename = params.baseUrl.substring(0, params.baseUrl.length - 1); // Remove trailing slash
const urlAfterBasename = params.url.substring(basename.length);
const store = configureStore(createMemoryHistory());
store.dispatch(replace(urlAfterBasename));
// Prepare an instance of the application and perform an inital render that will
// cause any async tasks (e.g., data access) to begin
const routerContext: any = {};
const app = (
<Provider store={ store }>
<StaticRouter basename={ basename }
context={ routerContext }
location={ params.location.path }
children={ routes } />
</Provider>
);
renderToString(app);
// If there's a redirection, just send this information back to the host application
if (routerContext.url) {
resolve({ redirectUrl: routerContext.url });
return;
}
// Once any async tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
resolve({
html: renderToString(app),
globals: { initialReduxState: store.getState() }
});
}, reject); // Also propagate any errors back into the host application
});
});
This is the Layout file:
import * as React from 'react';
import {RouteComponentProps} from 'react-router-dom';
import {withRouter} from 'react-router';
import {connect} from 'react-redux';
import * as actions from '../Features/LanguageChanger/actions/languageChanger';
import LayoutProps from '../propertyTypes/LayoutProps';
import {ApplicationState} from '../store/initialState';
import Navbar from '../Features/Navbar/Navbar';
import NavbarContainer from '../containers/NavbarContainer';
class Layout extends React.Component<LayoutProps, {}> {
public render() {
return <div className='main-container'>
<NavbarContainer {...this.props}/>
{this.props.children}
</div>;
}
}
export default withRouter(connect(
(state: ApplicationState) => state.language,
actions.actionCreators,
)(Layout));
And the routes file:
import * as React from 'react';
import {Route} from 'react-router-dom';
import {Switch} from 'react-router';
import {Redirect} from 'react-router';
import Layout from './components/Layout';
import Home from './Features/Home/Home';
import About from './Features/About/About';
import ProductInfo from './Features/ProductInfo/ProductInfo';
import Questions from './Features/Questions/Questions';
import Shop from './Features/Shop/Shop';
import Contacts from './Features/Contacts/Contacts';
export const routes =
<Layout>
<Switch>
<Route path='/:language/about' component={About}/>
<Route path='/:language/product-info' component={ProductInfo}/>
<Route path='/:language/questions' component={Questions}/>
<Route path='/:language/shop' component={Shop}/>
<Route path='/:language/contact-us' component={Contacts}/>
<Route path='/:language' component={Home}/>
<Route path='/' component={() => (<Redirect to={'/en'}/>)}/>
</Switch>
</Layout>;
I need the match object because I need to make requests based on the language in the URL. My languageChanger component is in the Navbar. My solution for the problem is to extract manually the things I nedd from the URL. Has anyone had the same problem?
To get the correct route props in your component you should wrap withRouter with connect
export default connect(/* ... */)(withRouter(Layout);
Which will update your component if the location changed and is not blocked by connect. (You can read more about in the React Router documentation
To get the placeholder :langauge from your path you have to wrap your component in a route component which has the pattern as path property.
Inside of your Layout component:
/* ... */
return (
<div className='main-container'>
<Router path="/:language" render={({ match ) => (
<NavbarContainer {...this.props} language={match.params.language} />
)} />
{this.props.children}
</div>;
);
/* ... */
This way the navbar will be only rendered if it matches the path pattern. You could change render to children but then you have to handle a possible match which is null. React Router has a nice documentation about the possibilities of Route.
Anyway you have to make sure that the value of match.params.language is a real language code because somebody could enter the path /foo in the address bar and your language will be foo.
Context: React app with React Router 4.0
Now that the best practice changed and you should not nest routes, but rather include them in Components as seen in the example, I'm wondering how this will turn out in the long run once the app grows. Will it not be superhard to follow the routes / components if you don't have one central place to look for them?
Have you used rr-4 for bigger apps? What's the best practice?
Pretty scary as I already had to do a lot of refactoring moving from rr-2 to rr-4, I wouldn't want to redo everything when it's much more expensive.
meet the problem too, here is the solution that migrate from react-router 3.0:
app.js
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import routes from './routes';
import createBrowserHistory from 'history/createBrowserHistory';
import injectTapEventPlugin from 'react-tap-event-plugin';
import "./css/style.less";
import {Provider} from 'react-redux';
import {ConnectedRouter, routerMiddleware} from 'react-router-redux';
import {Route} from 'react-router';
import store from './store';
import App from 'components/App';
import {load} from 'lib/utils';
import Dispatcher from 'views/Dispatcher';
injectTapEventPlugin();
export const browserHistory = createBrowserHistory();
export const middleware = routerMiddleware(browserHistory);
class MainApp extends React.Component {
render() {
return (
<Provider store={ store }>
<ConnectedRouter history={ browserHistory }>
<Route path="/" render={ (props) => {
routes.onEnter(props);
const { match } = props;
return (
<App>
<Route path={match.url} exact component={ load("Home") }/>
{
routes.childRoutes.map((route,idx) => {
return <Route
path={`${match.url}/${route.path}`} component={ route.component }
key={idx}
exact={ !!route.exact }
/>
})
}
</App>
);
} }/>
</ConnectedRouter>
</Provider>
)
}
}
ReactDOM.render(<MainApp />, document.getElementById("app"));
router.js
import React from 'react';
import App from 'components/App';
const load = (moduleName) => {
const module = require(`views/${moduleName}/index.js`);
return module.default;
};
export default {
path: '/',
component: App,
indexRoute: { component: load('Home'), },
childRoutes: [
{ path: 'home', component: load('Home'), }
]
}