React how to check props before passing in another component - reactjs

Lets say I have a component <MyComponent check={this.props} />. This will call MyComponent class. Here I am doing something like this:
render(){
const {user} = this.props
return (){
<div>Welcome {user.name}</div>
}
}
In my container initially there is no user.name when the user is logged in then only user.name is available in props. In componentWillMount I have checked for isLoggedIn and if not I'm redirecting it, but in render method it checks for user.name property which is not available before login.
If isLoggedIn is true then <MyComponent check={this.props} /> which calls my MyComponent should return a redirection or other.
Does anyone have any idea how to implement this?

Your are able to check your props in componentWillReceiveProps method. It takes nextProps as a parameter.
class Parent extends React.Component {
constructor(props){
this.state = {
increment: 0
}
this.myClick = this.myClick.bind(this)
}
myClick(){
this.setState({
increment: this.state.increment + 1
})
}
render(){
return <div>
<Child number={this.state.increment}/>
<button onClick={this.myClick}>Click And Get Props</button>
</div>
}
}
class Child extends React.Component {
constructor(props){
console.log(props)
super(props)
this.state = {
count: props.number
}
}
componentWillReceiveProps(nextProps){
console.log(nextProps.number)
if(nextProps.number % 2) this.setState({count: nextProps.number})
}
render(){
return <div>
<span>Props: {this.props.number}</span><br/>
<span>State: {this.state.count}</span>
</div>
}
}
React.render(<Parent />, document.getElementById('container'));
Fiddle Example
Also please have a look at this link i hope it will help you.
Thanks

Use check inside render method, in your case
componentWillMount(){
this.state = {
isLoggedIn: this.props.isLoggedIn
}
}
componentWillReceiveProps(nextProps){
this.setState({isLoggedIn: nextProps.isLoggedIn})
}
render(){
return (
this.props.isLoggedIn && <MyComponent check={this.props} />
)
}

Related

How can I correctly pass state as props from one component to another?

I'm trying to pass my state as props from component Locatione.js to Map.js, so the props are available when I call the function SendLocation in Map.js.
Here is my component Locatione
export default class Locatione extends Component {
state = {
location: null
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let location = await Location.getCurrentPositionAsync({ });
this.setState({ location });
console.log("log this pls", this.state); // the state here logs correctly
};
render() {
return (
<Map locatione={this.state} /> // when accesing this props in Map, I'm getting **null**
);
}
}
Here is my Map.js component
export default class Map extends React.Component {
sendLocation() {
console.log("sending location log", this.props); // the props here appear as null
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, () => console.log("hi", this.props))} //the props here log correctly
/>
);
}
}
I also tried passing my props in this fashion, to no avail.
export default class Map extends React.Component {
sendLocation(altitude, longitude) {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, (this.props)))}
/>
);
}
}
Thanks for your help
There is a little problem here:
onPress={(this.sendLocation, () => console.log("hi", this.props))}
The console.log will trigger everytime the code renders or re-renders the button, not when you click it.
If you want to log after you call a function change the onPress to:
onPress={() => {
this.sendLocation()
console.log("hi", this.props)
}}
The other problem is that you are not giving your sendLocation function access to this.
You have two ways of doing it:
First way: Binding it inside your constructor. So inside your Map.js you add it like:
constructor(props){
super(props);
this.sendLocation.bind(this);
}
Second way: Declaring your sendLocation function as an arrow function:
sendLocation = () => {
console.log("sending location log", this.props);
}
Just as you can pass regular values as props, you can also grab data from a component’s state and pass it down as props for any of its child components. You just need to pass the exact value, also use constructor in case of class components.
`export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
location: null
};
}
render() {
return (
<Map location={this.state.location} />
);
}
}`
You need to pass the function to onPress and use arrow function to be able to use this inside sendLocation.
class Map extends React.Component {
sendLocation = () => {
console.log('sending location log', this.props.locatione); // the props here appear as null
};
render() {
return (
<Button
title="Send Sonar"
onPress={this.sendLocation}
/>
);
}
}
You are passing the props through components correctly, but you should use arrow function and also anonymous func.
Try:
export default class Map extends React.Component {
sendLocation = (altitude, longitude) => {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={()=>this.sendLocation}
/>
);
}
}

Child not updating after Parent State changed

I am quite new with React and I have problem bellow
I have a parent component like this:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return false;
}
setCount = () => {
this.setState({
count: 2
});
};
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test count={this.state.count}/>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}
And here is Test component
class Test extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return true;
}
render() {
const {count} = this.props;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
</div>
);
}
}
I have method "shouldComponentUpdate" returns "false" in Parent component because I don't want to re-render it.
My understanding is React know which part of DOM need to be re-rendered. And in this case, the state of Parent changes will re-render "Test" component
But when I run above code, "Test" component does not redender.
Is there anything wrong in my code?
Thanks a lot for your help
You need to return true from your parent's shouldComponentUpdate method.
If you return false, after the initial render it won't update, even if you call a function that calls setState.
Is the refresh of the whole page are you talking about? If thats the case, probably you wanna change your <a> tag to button or use e.preventDefault();.
If not, I am not sure if that is possible. If you setState in the parent, it will rerender parent as well as the children. If you dont want to render the parent then you have to manage individual state management in the child level.
For example,
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const {name, running, onRun, onStop} = this.props;
return (
<div>
<Test/>
</div>
);
}
}
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {count:1};
}
setCount = (e) => {
e.preventDefault();
this.setState({
count: 2
});
};
render() {
const {count} = this.state;
return (
<div>
{console.log("Counting")}
<p>{count}</p>
<p><a href="#" onClick={this.setCount}>SetCount</a></p>
</div>
);
}
}

How to setState for all instances of the same component type in that component

How to setState for all instances of the same component type in that component.
In ParentComponent
render() {
return(
<ChildComponent ... />
<ChildComponent ... />
<ChildComponent ... />
);
}
In ChildComponent
//onClick Handler should set state of all instances
onClick() {
this.setState({value: ''})
}
If you have some value which is used by multiple child components, then the correct way is to take that value one level up (i.e. in parent) and pass that value to those child as prop so that all children share the same value. So maintain a state in parent and pass them as prop to children like this
onClick() {
this.setState({value: ''})
}
render() {
return(
<ChildComponent value={this.state.value} onClick={this.onClick}... />
<ChildComponent value={this.state.value} onClick={this.onClick}... />
);
}
Ok, so...
I'm working on some kind of picker.
There are 3 components of the same type. Each component stores different state.
States depends on what user typed in input field (combined with react-autosuggest).
User fill up 3 inputs, and choose 1 image that is rendered depends on state.
After user click image, all inputs should be cleared (value is in state).
I made that working but not rly satisfied
and this is combined with redux
Parent
I made a ref to each component and save a instance to his state and pass callback to trigger methods in all child instances.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {};
this.clearAllInputs = this.clearAllInputs.bind(this) // coz callback returns child own props
}
componentDidMount() {
this.setState({
firstChild: this.firstChild.getWrappedInstance(),
secondChild: this.secondChild.getWrappedInstance(),
thirdChild: this.thirdChild.getWrappedInstance(),
})
}
clearAllInputs() {
//call methods from all child instances
this.state.firstChild.clearInput();
this.state.secondChild.clearInput();
this.state.thirdChild.clearInput();
}
...
render() {
return(
<Child ref={ context => this.firstChild = context } clearAllInputs={this.clearAllInputs} ... />
<Child ref={ context => this.secondChild = context } clearAllInputs={this.clearAllInputs} ... />
<Child ref={ context => this.thirdChild = context } clearAllInputs={this.clearAllInputs} ... />
);
}
...
}
class Child extends Component {
...
clearInput() {
this.setState( { value : '' } );
}
render() {
return(
...
<img ... onClick={ this.props.clearAllInputs } />
);
}
}
export default connect(state, null, dispatchers, { withRef: true })(Child);
Since you want the same state in all of the child instances, I'd say that what you want to do is actually set the state in the parent, then pass that into all of the children as a prop. You'll need a click handler method in the parent, which you'll pass to the children as well.
Ok, I haven't tested this code, but the basic logic will be something like:
Parent
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {
"value": "" // assuming 'value' is a string
}
}
handleClick(value) {
this.setState({ "value": value })
}
render() {
return(
<ChildComponent
handleClick={this.handleClick}
value={this.state.value} />
<ChildComponent
handleClick={this.handleClick}
value={this.state.value} />
<ChildComponent
handleClick={this.handleClick}
value={this.state.value} />
)
}
Child (since you talk about state of a child, setting this up as though it is a stateful component, not a presentational component)
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {
"value": "" // assuming 'value' is a string
}
}
componentWillReceiveProps(nextProps) {
this.setState( {"value": nextProps.value} )
}
handleClick() {
const value = "Hey here's a value!"
props.handleClick(value) // call the parent's handleClick
}
render() {
return(
<div>
<button onClick={this.handleClick}>Set value</button>
</div>
)
}
But truth be told, I wouldn't even bother setting the state in the child - just set it in the parent and access it via props.

A more elegant way of using Reactjs to redirect a page without setTimout and causing Warning: setState(...): Can only update

I have a simple react + redux login page. When auth is successful i redirect the user to home page. This initially brought up an issues of "Warning: setState(...): Can only update...". After searching around, a proposed solution was to setTimeout on the redirect function to give a small delay in order for the states to be set.
I am looking for a more elegant way to redirect a user without using setTimeout as it can be a little unpredictable depending on situation and without causing "setState(...)" warning .
Below is a condensed version of the code:
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Success {(setTimeout(() => this.goHome()), 1)}</div>
)}
</form></div>
);
}
goHome = function() {
this.props.history.push('/');
};
Since isLoginSuccess is a prop, you could check for it in componentWillReceiveProps function and redirect from there
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
componentWillReceiveProps(nextProps) {
const {isLoginSuccess} = this.props;
if(!isLoginSuccess && nextProps.isLoginSuccess) {
this.props.history.push('/');
}
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Success </div>
)}
</form></div>
);
}
If you are not using React-Router, you can rewrite your code to use other life-cycle events, specifically componentDidMount to do this. Do not cause any state changes in render.
Example:
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
//...
};
}
goHome() {
this.props.history.push('/');
}
componentDidMount(){
let {isLoginSuccess } = this.props;
if (isLoginSuccess){
this.goHome();
}
}
render() {
let {isLoginSuccess } = this.props;
return (
<div><form>
//...
{isLoginSuccess && (
<div>Logged-in successfully, please wait while we take you to home...</div>
)}
</form></div>
);
}
}

React.js, why my class component doesn't rerender the element, but functional component can work?

I'm new to react.js, just follow the tutorial. Here is my code. At first, i tried to use the class Component 'Greeting' to let it show different words after
clicked the button, but i don't know what's wrong, it doesn't rerender the element, and the construtor() method of Greeting only called once. The commented out code functional Component 'Greeting' works well. Not sure what's the difference :(
class GreetingGuest extends React.Component {
render() {
return (
<h3>hello Guest, Click login button !!! </h3>
);
}
}
class GreetingUser extends React.Component {
render() {
return (
<h3>You have logged in, welcome !!!</h3>
);
}
}
class Greeting extends React.Component {
constructor(props) {
super(props);
console.log('Greeting.state.is_logon = ', props.is_logon);
this.state = {is_logon: props.is_logon};
}
render() {
let welcome_msg = null;
if (this.state.is_logon) {
welcome_msg = <GreetingUser />;
}else {
welcome_msg = <GreetingGuest />;
}
return welcome_msg;
}
}
//function Greeting(props) {
// const is_logon = props.is_logon;
// if (is_logon) {
// return <GreetingUser />;
// }
// return <GreetingGuest />;
//}
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {is_logon: false};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
is_logon: !prevState.is_logon
}));
}
render() {
let button = null;
let greeting = null;
if (this.state.is_logon) {
button = (
<button onClick={this.handleClick}>Logout</button>
);
greeting = <Greeting is_logon={this.state.is_logon} />
}else {
button = (
<button onClick={this.handleClick}>Login</button>
);
greeting = <Greeting is_logon={this.state.is_logon} />
}
return (
<div>
{greeting}
{button}
</div>
);
}
}
ReactDOM.render(
<LoginComponent />,
document.getElementById('Login')
)
HTML:
<html>
<body>
<div id="Login"></div>
</body>
<html>
The reason the class component doesn't re render, is because you have stored the logged_in prop in state from the constructor, and the constructor is only called once. Also state can only be modified from within the component.
To fix this you have 2 options;
Use componentWillReceiveProps, and update the local state with the new logged_in prop.
componentWillReceiveProps(nextProps) {
if (nextProps.logged_in !== this.state.logged_in) {
this.setState({ logged_in: nextProps.logged_in });
}
}
Or; do not use state but use the prop directly.
render() {
let welcome_msg = null;
if (this.props.is_logon) {
welcome_msg = <GreetingUser />;
}else {
welcome_msg = <GreetingGuest />;
}
return welcome_msg;
}
Where I think you should use the latter, since the parent component already maintains state.
Well to be honest the answer which I posted previously was wrong. It was because the way you posted the question telling that everything works fine when function based component is added. Then I created a project using your code and figured out few issues in your code.
Firstly you are maintaining state locally outside redux here. You are passing down the state of the login from the parent LoginComponent to the child component called Greeting like this.
greeting = <Greeting is_logon={this.state.is_logon} />
This gets passed as a props to the child component which is Greeting in this case. Remember React uses one way data flow.
Now from that child component you can access the data using this.props as shown below. You don't need to maintain any local state what so ever there.
Do the following changes in your Greeting component.
constructor(props) {
super(props);
}
Then make sure you access the values from this.props instead of any local state object like this.
render() {
let welcome_msg = null;
if (this.props.is_logon) {
welcome_msg = <GreetingUser />;
}else {
welcome_msg = <GreetingGuest />;
}
return welcome_msg;
}
}
This solved the issue. Happy Coding !

Resources