react-router root onChange to replace path infinite - react-router

It seems if I change path in root onEnter or onChange hook, the url will change infinite. But if I change path in child routes, it will work. Actually I want to handle the authentication in one place, otherwise every child route should handle the same logic.
{
path: '/',
onChange: function(prevState, nextState, replace, callback) {
if(!logined) {
replace('login');
}
},
childRoutes: [
....
]
}

It changes infinitly because onChange invokes on replace
try
onChange: function(prevState, {location:{pathname:next}}, replace)=> {
if(!logined && next !== '/login') {
replace('/login');
}
}
also to handle the authentication in one place, you can use HOC, something like that
const CheckAuth = (isLogined, redirectPath) => Component =>
class CheckAuth extends React.Component {
componentWillUpdate(nextProps, nextState){
//Check auth on change
this.checkAuth(nextProps);
}
componentWillMount(){
//Check auth on enter
this.checkAuth(this.props);
}
checkAuth({location}){
if(!isLogined && location.pathname!==redirectPath) {
browserHistory.replace(redirectPath);
}
}
render(){
return (<Component {...this.props}/>);
}
};
and App component
class App extends React.Component { ... }
export default CheckAuth(isLogined,'/login')(App);
also, there is a way with redux-auth-wrapper

Related

How do I delay Angular doCheck execution until user's value is fully entered into input?

I am using the follow doCheck() method to determine if the user's input has changed before processing it.
ngDoCheck() {
if (this.filter.price !== this.oldPrice) {
// this.changeDetected = true;
console.log(`DoCheck: Price changed to "${this.filter.price}" from
"${this.oldPrice}"`);
this.oldPrice = this.filter.price
}
}
The problem is ngDoCheck is called for each individual digit the user enters. I prefer to have the user complete their input before processing it like is done using debounceTime in rxjs.
If it is user input coming from a FormControl I would advise subscribing to its valueChanges observable with a debounceTime operator instead. But if you insist you can also use each call of the ngDoCheck to place the next value into your own observable:
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
class YourComponent {
private changeSubject = new Subject();
ngOnInit() {
this.changeSubject.pipe(
distinctUntilChanged(),
debounceTime(400)
).subscribe( value => {
console.log('debounced value:', value);
});
}
ngDoCheck() {
this.changeSubject.next(this.filter.price);
}
}
Use changes instead of a lifecycle hook.
HTML:
<input (change)="change($event)">
component:
change(newValue) {
console.log(newValue);
}

Check current component after timeout

I'm in a component called "recoger_success" and I want it to navigate to the home component after 10 seconds after clicking a button that has countdown() linked to it. The problem is that if I navigate to another component before the timeout gets to 0 then after the 10 seconds it doesn't matter in which component I am, it will always go to home component. I don't want this I want it only to go to the home component if the user is still on the timeout component otherwise ignore it..
I have tryed this to know the url of the actual component but it doesn't work..
countdown(){
setTimeout(() => {
if (this.route.snapshot.url.toString() == 'recoger_success'){
this.router.navigate([""]);
}
}, 10000);
}
apreciate any help.
Assign timeout to a variable and at the time you manually exist the page, clear the timeout
countdown(){
this.timeout = setTimeout(() => {
if (this.route.snapshot.url.toString() == 'recoger_success'){
this.router.navigate([""]);
}
}, 10000);
}
function myStopFunction() {
clearTimeout(this.timeout);
}
You need to bind the setTimeout to a variable.
component.ts
import { Component, OnDestroy } from '#angular/core';
export class Component implements OnDestroy {
countdownTimer: any;
countdown() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
}
this.countdownTimer = setTimeout(() => {
if (this.route.snapshot.url.toString() == 'recoger_success') {
this.router.navigate([""]);
}
}, 10000);
}
ngOnDestroy() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
}
}
}

How to remove loader when entering escape in react?

I need to come out from page after entering escape button , when the page takes much time for loading. I need to exit that loading when it takes longer time
For loading I am using
dispatcher.dispatch({
type:'Loader',
showLoader: true
})
You will have to add an event listener i.e. keyup or keydown. When any key is pressed, just compare its keyCode with escape button's keycode i.e. 27.
In react, event listener should be added in componentDidMount and removed in componentWillUnmount.
Here is an example. You can modify logic according to your requirements.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
loading: true,
}
this.onKeyUp = this.onKeyUp.bind(this)
}
componentDidMount() {
document.body.addEventListener('keyup', this.onKeyUp)
}
componentWillUnmount() {
document.body.removeEventListener('keyup', this.onKeyUp)
}
onKeyUp(e) {
if (/27/.test(e.keyCode)) {
this.setState({ loading: false })
}
}
render() {
if (this.state.loading) {
return <div>Loading</div>
}
return <div>Loaded</div>
}
}
Hope it helps.

Invoke function from React component declared as variable

How to invoke React component's function when this component is given in variable? I have a Parent that passes Test class into Child component, and this child wants to change something in Test.
export class Parent extends React.Component {
render() {
let test = (<Test />);
return (<Child tester={test} />);
}
}
export class Child extends React.Component {
render() {
this.props.tester.setText("qwerty"); // how to invoke setText, setState or something like that?
return ({this.props.tester});
}
}
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
text: this.props.text || ""
};
}
setText(text) {
this.setState({ text: text });
}
render() {
return (<div>{this.state.text}</div>);
}
}
I think you should think about life cycle of react components.
Please try the code below(I just added logging), and observe logs carefully.
export class Parent extends React.Component {
render() {
let test = (<Test />);
return (<Child tester={test} />);
}
}
export class Child extends React.Component {
render() {
console.log("Child render"); // <= logging added!
// this.props.tester.setText("qwerty");
// What kind of object is 'this.props.tester(= <Test />)' here???
return ({this.props.tester});
}
}
export class Test extends React.Component {
constructor(props) {
super(props);
console.log("Test constructor"); // <= logging added!
this.state = {
text: this.props.text || ""
};
}
setText(text) {
// this.setState({ text: text });
// this is another problem. We cannot call setState before mounted.
this.state.text= text;
}
render() {
return (<div>{this.state.text}</div>);
}
}
If so, you will see 2 important facts.
'Test' component is not instantiated yet, when you call 'setText'.
How can we call a method of object which is not instantiated? Cannot!
this means 'this.props.tester' is not an instance of 'Test' component.
But if you really want to exec your code, modify Child.render like this.
render() {
var test = new Test({props:{}});
// or even this can work, but I don't know this is right thing
// var test = new this.props.tester.type({props:{}});
test.setText("qwerty");
return test.render();
}
But I don't think this is a good way.
From another point of view, one may come up with an idea like,
render() {
// Here, this.props.tester == <Test />
this.props.tester.props.text = "qwerty";
return (this.props.tester);
}
but of course it's not possible, because 'this.props.tester' is read-only property for Child.

How to access JSON in a React Component?

I have a JSON object from a script tag like so:
<script type="text/json" id="json-data">
{'someData': 'Lorem ipsum...'}
</script>
I would like to be able to pull this information and use it within a React component in my render method.
The issue seems to be that I need to set this to a variable within componentWillMount:
export default MyReactComponent extends Component {
componentWillMount() {
const test = document.getElementById('json-data').innerHTML;
}
render() {
return (
<div>
// This is where I would like to use this data.
</div>
);
}
}
Is this the best way to handle passing this data? If so, how can I access this data within the render method of my component?
Thanks!
Store it in the component's state. The render method should only depend this.state and this.props
At the risk of oversimplifying:
this.props are passed from parent components
this.state is state that is internal to the component
Example
export default MyReactComponent extends Component {
componentDidMount() {
this.setState({
test: JSON.parse(document.getElementById('json-data').innerHTML)
});
}
render() {
return <div>{this.state.test}</div>;
},
getInitialState: function() {
return {test: {}}
}
}