I am creating Higher Order Components in React and making sure that user cannot access the protected routes. Everything works fine but I am unsure where to put the code for checking the redux state. Currently, in the code below I have placed all the code in componentDidMount. This is because componentWillMount is deprecated. Is this the best place to check because componentDidMount is triggered after the component has been mounted.
import React, { Component } from 'react';
import { connect } from 'react-redux'
export default function(ComposedComponent) {
class Authenticate extends Component {
componentDidMount() {
if(!this.props.isAuthenticated) {
this.props.history.push('/')
}
}
render() {
return (
<ComposedComponent {...this.props} />
)
}
}
const mapStateToProps = (state) => {
return {
isAuthenticated: state.isAuthenticated
}
}
return connect(mapStateToProps)(Authenticate)
}
Assuming the correct state is available at construction, you can do the redirect in the constructor:
class Authenticate extends Component {
constructor(props) {
super(props);
if(!props.isAuthenticated) {
props.history.push('/')
}
}
...
}
This is the purpose of React Router Redirect component:
render() {
return !this.props.isAuthenticated ? (
<Redirect to="/" />
) : (
<ComposedComponent {...this.props} />
)
}
Related
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);
I am new to React and trying to make context API. I have read some similar question but I can not get a solution.
My context provider file :
import React, { Component } from 'react'
const MyContext = React.createContext();
class ContextProvider extends Component {
constructor(props) {
super(props)
this.state = {
isLogin: false
}
}
handleLogin = () => {
this.setState({
isLogin : true
})
}
render() {
return (
<MyContext.Provider value={{
...this.state,
handleLogin : this.handleLogin
}}>
{this.props.children}
</MyContext.Provider>
);
}
}
const ContextConsumer = MyContext.Consumer;
export {ContextProvider, ContextConsumer};
I need to change the state by accessing handleLogin() in the ContextProvider.js after user successfull login :
import React, { Component } from 'react'
import {ContextConsumer} from "./ContextProvider";
class Login extends Component {
onHandleSubmit = () => {
// on submit login success :
// --- how to call handleLogin() in ContextProvider.js here ? ----
}
render() {
return (
<div> --- not expected here ---- </div>
)
}
}
BTW, sorry for my English.
Assuming your Login component is wrapped by the ContextProvider higher up in the hierarchy, you can access context inside class component by define a static contextType .
For that you need to export context from ContextProvider first like
export {ContextProvider, ContextConsumer, MyContext };
and then use it like
import React, { Component } from 'react'
import {MyContext} from "./ContextProvider";
class Login extends Component {
static contextType = MyContext;
onHandleSubmit = () => {
// on submit login success :
this.context.handleLogin();
}
render() {
return (
<div> {/* render content here */} </div>
)
}
}
However if you are using a version of react between 16.3.0 and 16.6.0, you need to pass on context using render props pattern like
class Login extends Component {
onHandleSubmit = () => {
// on submit login success :
this.props.context.handleLogin();
}
render() {
return (
<div> --- not expected here ---- </div>
)
}
}
export default (props) => (
<ContextConsumer>
{values=> <Login {...props} context={values} />}
</ContextConsumer>
)
I am trying to build a Higher Order Component that would be used to protect routes.It would check (via a network call, not from cache) for the current user, and if not found, would redirect to /login.
Any ideas on how to do so?
First understand how React HOC works. I'll try to answer this using the pseudo code.
HOC.jsx
export default (Component) => class HOC extends React.Component {
constructor(){
super()
this.state = {
waiting: true,
}
}
componentDidMount() {
this.props.actions.validateUser() // your network call to validate user
.then(() => {
// do your stuff
this.setState({
waiting: false,
})
})
.catch(() => {
// handle redirect using react-router
})
}
render() {
if(this.state.waiting) {
// Add a loader here
return <h1>Loading...</h1>
}
return <Component {...this.props} />
}
}
Component.jsx
import HOC from './HOC.jsx'
class Comp extends React.Component {
render() {
return <h1>I'm a valid user</h1>
}
}
export default HOC(Comp)
When any component uses the HOC, the network call will be executed on mount (can be moved to componentWillMount as well) and based on the response the component will be rendered.
Hope this helps!
I have this piece of code (which I've simplified for posting here) that creates a component and renders it
const getComponentToRender = (user, path) => {
switch(path ){
case 'ChangePassword':
return <ChangePassword user={ user } />;
case 'NewPassword':
return <NewPassword user={ user } />;
case 'PasswordExpire':
return <PasswordExpire user={ user } />;
default:
return null;
}
}
class UserAdmin extends React.Component {
static propTypes = {
user: PropTypes.object.isRequired
};
render() {
const component = getComponentToRender(this.props.user, 'ChangePassword' );
return(
<div id='user-admin-wrapper'>
{component}
</div>
)
}
componentWillUnmount(){
}
}
When I navigate away from UserAdmin the componentWillUnmount gets called.
Q: What is the simplest way to actually remove the component ChangePassword or any other component (by its name) from the DOM when componentWillUnmount executes.
OR, removing the component at any point, without waiting for componentWillUnmount
Using react-dom 15.6.1 . btw
Un-mounting a component will un-mount(remove) all the child components it contains. So after componentWillUnmount the component you rendered inside it will be removed.
If you need to control over components that rendered without un-mounting you use conditional render logic.
Example
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
shouldIRender: true
};
}
componentDidMount() {
setTimeout(() => {
this.setState({shouldIRender: false});
}, 5000);
}
render() {
return(
<div>
<ComponentThatAlwaysHere />
{ this.state.shouldIRender === true ? <ComponentThatRemovesAfterStateChange /> : null }
{ this.state.shouldIRender === true && <AnotherComponentThatRemovesAfterStateChange /> }
</div>
)
}
}
I am performing authentication on my top level route which I have named App in using react-router. My app is using the following
<div className="app-wrapper">
<NavbarC currentUser={this.state.currentUser} />
{this.props.children}
</div>
I want to be able to pass this.state.currentUser to the the this.prop.children.
Is this possible or should I just create a new state using the UserStore I've created?
Since I'm using flux, it seems the proper way to handle it is to pull the data from the store for each route. My route looks like the following:
let getCurrentUserFromStore = () => {
return { currentUser: UserStore.getCurrentUser() };
}
export default class Landing extends React.Component{
constructor(props) {
super(props);
this.state = getCurrentUserFromStore();
this.onStoreChange = this.onStoreChange.bind(this);
}
onStoreChange() {
this.setState(getCurrentUserFromStore());
}
componentDidMount() {
UserStore.addChangeListener(this.onStoreChange);
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onStoreChange);
}
render() {
if ($.isEmptyObject(this.state.currentUser)) {
return <LoginPage />
} else {
return <DashboardPage currentUser={this.state.currentUser} />
}
}
}