When you open reactjs, under "Declarative" header, there is a sentence: React will efficiently update and render just the right ponents when your data changes.
For a couple of my apps, I'm using the following structure: App | AppContainer(all the app logic, protected before login) | Login(Login form)
This structure works well if you return 2 different ponents inside App's render
, according to the user's credentials.
render(){
if(isUserLoggedIn()){
return <AppContainer />;
}
return <Login />;
}
Inside the Login
ponent, I'm refreshing the page with window.location.reload
so the App's render
will be triggered, and I'll get the AppContainer
ponent.
But it feels a little like jQuery + Angular. Is there a better (more React) way to trigger render function, or is this how things should be?
When you open reactjs, under "Declarative" header, there is a sentence: React will efficiently update and render just the right ponents when your data changes.
For a couple of my apps, I'm using the following structure: App | AppContainer(all the app logic, protected before login) | Login(Login form)
This structure works well if you return 2 different ponents inside App's render
, according to the user's credentials.
render(){
if(isUserLoggedIn()){
return <AppContainer />;
}
return <Login />;
}
Inside the Login
ponent, I'm refreshing the page with window.location.reload
so the App's render
will be triggered, and I'll get the AppContainer
ponent.
But it feels a little like jQuery + Angular. Is there a better (more React) way to trigger render function, or is this how things should be?
Share Improve this question edited Nov 1, 2019 at 10:01 ResponsiblyUnranked 1,8763 gold badges22 silver badges37 bronze badges asked Nov 1, 2019 at 9:44 AnimusAnimus 8631 gold badge17 silver badges28 bronze badges 3-
if there is a state update happening in
App
ponent, the render will be called – Agney Commented Nov 1, 2019 at 9:46 -
yes , you can do so , just maintain a
isAuthenticated
state in your redux, and that way conditionally , you render will be triggered aagain – 0xAnon Commented Nov 1, 2019 at 9:52 -
Probably
react-router-dom
fits to what you need. – Joseph D. Commented Nov 1, 2019 at 10:16
3 Answers
Reset to default 3Is there a better(more React) way to trigger render function...
The usual way is to have state, in this case at minimum a boolean for whether the user is logged in, and update that state when the user logs in successfully or logs out. Updating state triggers rendering.
In your case, since you're using Redux, you'd probably have your state there.
I don't use Redux (yet?), this is vaguely what it would look like without, roughly (if you're using a class ponent as you seem to be):
class App extends Component {
constructor(props) {
super(props);
this.state = {
loggedIn: /*...initial value, perhaps from web storage or cookie...*/;
};
this.onLogin = this.onLogin.bind(this);
this.onLogout = this.onLogout.bind(this);
}
onLogin() {
// ...probably stuff here, then:
this.setState({loggedIn: true});
}
onLogout() {
// ...probably stuff here, then:
this.setState({loggedIn: false});
}
render() {
if (this.state.logedIn) {
return <AppComponent onLogout={this.onLogout}/>;
}
return <Login onLogin={this.onLogin}/>;
}
}
or with hooks:
const App = () => {
const [loggedIn, setLoggedIn] = useState(/*...initial value, perhaps from web storage or cookie...*/);
const onLogin = useCallback(() => {
// ...probably stuff here, then:
setLoggedIn(true);
}, [loggedIn]);
const onLogout = useCallback(() => {
// ...probably stuff here, then:
setLoggedIn(false);
}, [loggedIn]);
if (this.state.logedIn) {
return <AppComponent onLogout={onLogout}/>;
}
return <Login onLogin={onLogin}/>;
}
(again, roughly)
If you need to update the ponent state, then you can pass an observable and listen for changes or use some state management library.
Here is one possible solution:
- Create observable class
declare type IObserverHandler = (event: any) => void;
export class Observable {
private observers: IObserverHandler[] = [];
public subscribe(observer: IObserverHandler) {
if (!this.observers.includes(observer)) {
this.observers.push(observer);
}
}
public unsubscribe(observer: IObserverHandler) {
this.observers = this.observers.filter(o => o !== observer);
}
public publish(event: any) {
for (const observer of this.observers) {
observer(event);
}
}
}
- Create Login class that will publish events on actions such as login or logout
class Login extends Observable {
public login() {
this.publish({ value: true });
}
public logout() {
this.publish({ value: false });
}
}
- In ponent subscribe to observer and update ponent state using event value
export abstract class Component extends React.Component<any, any> {
private observer: IObserverHandler;
private observable: Login;
constructor(props: any) {
super(props);
this.observable = this.props.observable;
this.state = { isAuthenticated: false }
this.observer = (event) => {
this.setState({ isAuthenticated: event.value })
}
this.observable.subscribe(this.observer);
}
ponentWillUnmount() {
this.observable.unsubscribe(this.observer);
}
}
You can use useNavigate
and navigate to the same url you are on. For example, instead of window.location.reload()
, you can say navigate("/...your current url....")
window.location.reload()
is not the best option everytime. It works on localhost, but for example on when you deploy it to the internet by using services such as "Netlify", it can can cause "not found url" error
Creating some extra state and tracking them for re-rendering your page might unnecessarily plicate your code.