How does React Router get into the React context? - reactjs

I am looking at the redux-blog-example. There is SignupRoute.js which looks like this:
#connect(state => ({
auth: state.auth
}), {
signup
})
export default class SignupRoute extends React.Component {
static contextTypes = {
router: React.PropTypes.object
}
handleSubmit = (email, password) => {
const router = this.context.router;
this.props.signup(email, password, router);
}
render() {
return (
<Signup
auth={this.props}
handleSubmit={this.handleSubmit}
/>
);
}
}
How does the router get wired up to the context of this class?

It uses context, an undocumented but quite widely implemented React feature. For a full lowdown see this article, but here's the gist of it:
let router = Router(); // just illustrating, this is not how you instantiate React router
class MyComponent extends React.Component {
static contextTypes = {
router: React.PropTypes.object
};
render(){
// By declaring context type here, and childContextTypes
// on the parent along with a function with how to get it,
// React will traverse up and look for the `router` context.
// It will then inject it into `this.context`, making it
// available here.
}
}
class Parent extends React.Component {
static childContextTypes = {
router: React.PropTypes.object
};
getChildContext(){
return {
router: this.props.router
};
}
render(){
return <MyComponent />;
}
}
ReactDOM.render(<Parent router={router} />, document.getElementById('app'));

Related

React JS - Pass Provider components methods to this.children

In React can methods be passed to {this.children} in a container consumer model. What I mean to ask is I have a provider component and I need to pass or refer the provider components methods in the child component.
export default class ContainerCompo extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
return (
{this.props.children}
}
}
export default class InputComponent extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
validate() {
ContainerCompo.methodOne(param)
}
render() {
return <InputComponent />
}
// Rendering the components
<ContainerCompo>
<InputComponent containerMethods={methods of ContainerCompo}/>
</ContainerCompo>
I hope my question is clear here, please suggest
First create a react context.
import React, { Component, createContext } from 'react';
// Create's authentication context to be use anywhere in the app
const ContainerContext = createContext();
export default ContainerContext;
Then create a provider for it.
export default class ContainerProvider extends Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
const { children } = this.props;
return (
<ContainerContext.Provider
value={{
container: {
methodOne: (...params) => this.methodOne(...params),
methodTwo: (...params) => this.methodTwo(...params)
}
}}
>
{children}
</ContainerContext.Provider>
)}}
Wrap your App with the provider.
import ContainerProvider from './ContainerProvider'
<ContainerProvider>
<App />
</ContainerProvider>
Then create a consumer for the context
export default function withContainer(InComponent) {
return function ContainerComponent(props) {
return (
<ContainerContext.Consumer>
{({ container }) => <InComponent {...props} container={container} />}
</ContainerContext.Consumer>
);
};
}
Then import the consumer and user in your components and you will get the methods as props
import withContainer from './ContainerConsumer'
render() {
const { container } = this.props;
return(<div />)
}
export default withContainer(YourComponent);

How can I obtain class props from url and store in react-redux

so I am trying to pass params using route to a react component and also at the same time use Component class props. Here is what am doing
import { loadSchemes, } from '../../actions/schemes;
export class Schemes extends Component {
constructor(props) {
super(props);
const { match: { params } } = this.props;
this.state = {
client_id: params.pk,
}
}
componentDidMount() {
this.props.loadSchemes();
}
render(){
return(
<div>
{this.props.schemes_list.map((scheme,index)=><p key={index}>{scheme}</p>)}
</div>
)
}
}
const mapStateToProps = (state) => ({
schemes_list: state.schemes,
});
export default connect(mapStateToProps,{ loadSchemes,})(Schemes);
And I have a url to this component as
<Route path="/client/:pk/schemes" component={Schemes}/>
The problem is I get an error this.props.schemes_list is undefined and this.props.loadSchemes is undefined
please help am using react-redux
Obviousely in component from where you call Scheme, you import { Schemes }, an unconnected component, instead of Schemes - default connected component. Please check it.

Redirect React Router from Context Provider

I'm new to React Router and trying to do a redirect from inside a provider using the new Conext API. basically my provider looks like this.
/* AuthContext.js */
class AuthProvider extends React.Component {
state = { isLoggedIn: false }
constructor() {
super()
this.login = this.login.bind(this)
this.logout = this.logout.bind(this)
}
login() {
this.setState({ isLoggedIn: true })
// Need to redirect to Dashboard Here
}
logout() {
this.setState({ isLoggedIn: false })
}
render() {
return (
<AuthContext.Provider
value={{
isLoggedIn: this.state.isLoggedIn,
login: this.login,
logout: this.logout
}}
>
{this.props.children}
</AuthContext.Provider>
)
}
}
const AuthConsumer = AuthContext.Consumer
export { AuthProvider, AuthConsumer }
I've read a lot about how to pass the history object using props and how to use a component but I can't see how these approaches would work here. My context provider sits at the top of the tree so it's not a child of the Router so I can't pass props. It's also not a standard component so I can't just insert a component, unless I've misunderstood something (which is very possible).
Edit: Looks like the way to go is withRouter, but how to export my AuthProvider in the code above so that history.push is available in my login function? As you can see I'm exporting multiple components wrapped in {} so can you wrap one of these in a HOC and do you have to explicitly pass history in or is it always available inside the component that's being wrapped?
use withRouter, sth like this to get access of history.
const AuthButton = withRouter( ({ history }) =>history.push("/"));
Try This:
import { Route } from "react-router-dom";
class AuthProvider extends React.Component {
yourFunction = () => {
doSomeAsyncAction(() =>
this.props.history.push('/dashboard')
)
}
render() {
return (
<div>
<Form onSubmit={ this.yourFunction } />
</div>
)
}
}
export default withRouter(AuthProvider);
Best explanation can be found here: Programmatically navigate using react router

How to navigate to different route programmatically in `redux-router5`?

I am using redux-router5 in my app to manage routes. I defined the route as below code:
const Root = () => (
<Provider store={store}>
<RouterProvider router={router}>
<MyComponent />
</RouterProvider>
</Provider>
);
router.start((err, state) => {
ReactDOM.render(<Root/>, document.getElementById('root'));
});
below is the code for store middlewares:
export default function configureStore(router) {
// create the middleware for the store
const createStoreWithMiddleware = applyMiddleware(
router5Middleware(router),
ReduxPromise,
ReduxThunk,
)(createStore);
const store = createStoreWithMiddleware(reducers);
router.usePlugin(reduxPlugin(store.dispatch));
return store;
}
In MyComponent, I can access a router instance through its property but it doesn't have navigate method for me to use. It only has route parameters, name, path, etc. So how can I navigate to a different route inside my component?
I've looked into an example
And it seems that it works like this:
import { connect } from 'react-redux';
import { actions } from 'redux-router5'
class SimpleComponent extends React.Component {
redirectToDashboard = () => this.props.navigateTo('/dashboard');
render() {
return (
<button onClick={this.redirectToDashboard}>go to dashboard</button>
)
}
}
export default connect(null, { navigateTo: actions.navigateTo })(SimpleComponent);
Original answer:
I don't know how to navigate with redux-router5 (at first glance to the documentation it is mainly meant to be used with redux) but to answer your question:
So how can I navigate to a different route inside my component?
Use withRouter HOC from 'react-router':
import { withRouter } from 'react-router'
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
redirectToDashboard = () => this.props.history.push('/dashboard');
render() {
const { match, location, history } = this.props
return (
<div onClick={this.redirectToDashboard}>You are now at {location.pathname}</div>
)
}
}
// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)
Worth to mention that if component is rendered by Route component then it is already "connected" and doesn't need withRouter.

Server-Side Auth Flow with React Router

I was wondering how to exactly implement authentication flow with React Router if my app is rendering server-side?
The scenario:
When the user first arrives at the app, we invoke an onEnter router method in order to check if any current user is present (localStorage), if they are we will redirect them to their dashboard, otherwise we redirect them to the landing page.
Therefore when my app hits my protected routes onEnter, here's ideally what should happen:
onEnter: () => {
if (localStorage.getItem('authToken')) {
// Authenticate and redirect user to intended path
} else {
// Redirect the user to the login page
}
}
The problem here is that because we're rendering server-side, we don't have access to things such as localStorage. Can anyone suggest a way to overcome this, or a better way to handle server-side auth with React Router?
You could use context, e.g:
import React, { Component, PropTypes, Children } from 'react'
class Provider extends Component {
static childContextTypes = {
isAuthed: PropTypes.bool
}
constructor(props) {
super(props);
this.initialData = isClient ? window.__initialData__ : props.initialData;
}
getChildContext() {
return {
isAuthed: this.initialData.isAuthed
};
}
render() {
let { children } = this.props;
return Children.only(children);
}
}
And wrap:
// The server creates this object:
const initialData = {
isAuthed: true
};
if (IS_CLIENT) {
ReactDOM.render(
<Provider>
<Router history={createHistory()}>{Routes}</Router>
</Provider>,
document.getElementById('app')
);
}
if (IS_SERVER) {
// Using renderToStaticMarkup/renderToString etc on:
<Provider initialData={initialData}>
<RoutingContext {...renderProps} />
</Provider>
}
You can then access it on the server or client from any component:
import React, { Component, PropTypes } from 'react'
class SomeComponent extends Component {
static contextTypes = {
isAuthed: PropTypes.bool
}
constructor(props, context) {
super(props, context);
console.log(context.isAuthed); // this.context outside ctor
}
}
Simple client check something like typeof document !== 'undefined'

Resources