Passing value to props reactjs - reactjs

I am trying pass value to my child components. The value that I am getting is coming from the an API that I called in my parent component and being called in the componentDidMount but the problem is the child components is not reading the props I am passing in his own componentDidMount, its only getting blank even in the reactdevtool it passing correct values. I solved this before but cannot remember what I did can you help. Thanks
Child:
componentDidMount() {
const {
events
} = this.props;
console.log(events)
}
Parent:
class App extends Component {
componentDidMount() {
let self = this;
GetAllMainItems().then(function(GetAllMainItemsResults) {
let MainObject = self.state.MainObject;
self.setState({
GetAllMainItemsResults
});
}
}
render() {
constructor() {
super();
this.state = {
MainObject: []
};
}
return ( <
div className = "App row" >
<
Calendar events = {
this.state.MainObject
}
/>
<
/div>
);
}

There are a few things you need to review.
constructor should be outside of render method.
You do not have to use let self = this. you can just do this.setState({...}) there.
Look at your GetAllMainItems callback. I don't know what you get
there. but you are definitely not setting mainObject in your state.
Instead, you will have this.state.GetAllMainItemsResults.
Recommendations
Try to understand object destructuring.
Use arrow functions
Hope it helps.
Parent Component
```
class App extends Component {
state = {
mainObject: ""
};
componentDidMount() {
GetAllMainItems().then(response => {
this.setState({
mainObject: response
});
});
}
render() {
const { mainObject } = this.state;
return (
<div className="App row">
<Calendar events={mainObject} />
</div>
);
}
}

The problem you are having is that your child component is re-rendering when it receives new events props.
Try adding a componentDidUpdate method to see these props updating:
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, prevState);
console.log('events:', prevProps.events, this.props.events);
}

Related

React - Class component doesn't receive any params

I have a simple class Component:
class SearchedUserWrapper extends Component {
constructor(props) {
super(props);
this.state = {
searchedPhrase: "",
pageNumber: 1
};
this.increment = this.increment.bind(this);
}
GetUserForSearchResult = (postAmount, pageNumber) => {
const list = [];
for (let index = 0; index < postAmount; index++) {
list.push(<SearchResultUser CurrentPage={pageNumber}></SearchResultUser>);
}
return list;
}
increment = () => {
this.setState({ pageNumber: this.state.pageNumber + 1 })
console.log(this.state.pageNumber + 0);
}
render() {
return (<div>
{this.GetUserForSearchResult(5, this.props.pageNumber)}
<Button onClick={this.increment}> Current page {this.state.pageNumber}</Button>
</div>);
}
}
and function GetUserForSearchResult receives a state from SearchUserWrapper class. My SearchResultUser looks like this:
class SearchResultUser extends Component {
render() {
{console.log(this.props.CurrentPage)}
return (
<div className="user-searchresult">
{this.props.CurrentPage}
</div>);
}
}
export default SearchResultUser;
And console log says that this props are undefined, and the div is empty.
My goal is to have the effect that everytime I click "Current page" button, to refresh all the SearchResultUser component so that it displays a state passed as parameter. What am I doing here wrong? Why does it says to be undefined?
EDIT:
I tried couple of things and discovered something.
If I send the state in the params directly, for example:
render() {
return (<div>
<SearchResultUser CurrentPage={this.state.pageNumber}></SearchResultUser>
</div>
It seems to work, but the order of sending the state to the function, which passes it to params of component doesn't work.
GetUserForSearchResult = (postAmount, pageNumber) => {
const list = [];
for (let index = 0; index < postAmount; index++) {
list.push(<SearchResultUser CurrentPage={pageNumber}></SearchResultUser>);
}
return list;
}
render() {
return (<div>
<SearchResultUser CurrentPage={this.state.pageNumber}></SearchResultUser>
</div>);
Can somebody explain why is it happening like this?
Edit:
In this place(below) I think you have to pass state instead of props, because the main component(SearchedUserWrapper) doesn't receive any props, so this is undefined.
{this.GetUserForSearchResult(5, this.props.pageNumber)}
https://codesandbox.io/s/stackoverflow-71594634-omvqpw
First message:
Did you check if the page number was updated?
If the next state depends on the current state, the doc of react recommend using the updater function form, instead:
this.setState((state) => {
return {quantity: state.quantity + 1};
});
You should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor at SearchResultUser

How to render updated state in react Js?

I am working on React Js in class component I am declaring some states and then getting data from API and changing that state to new value but React is not rendering that new value of state. but if I console.log() that state it gives me new value on console.
My code
class Home extends Component {
constructor(props) {
super(props);
this.state = {
unread: 0,
}
this.getUnread()
}
getUnread = async () => {
let data = await Chatapi.get(`count/${this.props.auth.user.id}/`).then(({ data }) => data);
this.setState({ unread: data.count });
console.log(this.state.unread)
}
render() {
const { auth } = this.props;
return (
<div>
{this.state.unread}
</div>
)
}
This is printing 2 on console but rendering 0 on screen. How can I get updated state(2) to render on screen.
and if I visit another page and then return to this page then it is rendering new value of state (2).
Please call getUnread() function in componentDidMount, something like this
componentDidMount() {
this.getUnread()
}
This is because in React class components, while calling setState you it is safer to not directly pass a value to set the state (and hence, re-render the component). This is because what happens that the state is set as commanded, but when the component is rerendered, once again the state is set back to initial value and that is what gets rendered
You can read this issue and its solution as given in react docs
You pass a function that sets the value.
So, code for setState would be
this.setState((state) => { unread: data.count });
Hence, your updated code would be :
class Home extends Component {
constructor(props) {
super(props);
this.state = {
unread: 0,
}
this.getUnread()
}
getUnread = async () => {
let data = await Chatapi.get(`count/${this.props.auth.user.id}/`).then(({ data }) => data);
this.setState((state) => { unread: data.count });
console.log(this.state.unread)
}
render() {
const { auth } = this.props;
return (
<div>
{this.state.unread}
</div>
)
}

React Unmount Base Component In Render-Prop Pattern

So Here is Some Sample Code Of What I Am Trying To Accomplish.
class NetworkCallHOC extends React.Component {
makeNetworkCall = () => {
this.setState({ isInNetworkCall: true });
// Make Some Network Call;
// On NetworkCall Complete;
this.setState({ isInNetworkCall: false });
}
state = {
isInNetworkCall: false,
makeNetworkCall: this.makeNetworkCall,
}
render() {
const { isInNetworkCall } = this.state;
if (isInNetworkCall) {
return // Some Loading screen;
} else {
const { children } = this.props;
return children(this.state)
}
}
}
This 'Higher Order Render Prop' takes a component as a child and passes down a function makeNetworkCall.
Now when makeNetworkCall is called the child component is unmounted and after the network call is completed a new child component is mounted back again and all the lifecycle methods of the child component are called.
Is there a way to avoid re-creating the the child component in such a scenario.
Thanks Regards.
A solution would be to always render the children, along with an optional loading screen that would display on top of it.
Something like:
render() {
const { isInNetworkCall } = this.state;
const { children } = this.props;
return <div>
{children(this.state)}
<Mask visible={isInNetworkCall}/>
</div>
}

How to get the DOM node from a Class Component ref with the React.createRef() API

I have these two components:
import { findDOMNode } from 'react-dom';
class Items extends Component {
constructor(props) {
super(props);
this.ref = React.createRef();
this.selectedItemRef = React.createRef();
}
componentDidMount() {
if (this.props.selectedItem) {
this.scrollToItem();
}
}
componentWillReceiveProps(nextProps) {
if (this.props.selectedItem !== nextProps.selectedItem) {
this.scrollToItem();
}
}
scrollToItem() {
const itemsRef = this.ref.current;
const itemRef = findDOMNode(this.selectedItemRef.current);
// Do scroll stuff here
}
render() {
return (
<div ref={this.ref}>
{this.props.items.map((item, index) => {
const itemProps = {
onClick: () => this.props.setSelectedItem(item.id)
};
if (item.id === this.props.selectedItem) {
itemProps.ref = this.selectedItemRef;
}
return <Item {...itemProps} />;
})}
</div>
);
}
}
Items.propTypes = {
items: PropTypes.array,
selectedItem: PropTypes.number,
setSelectedItem: PropTypes.func
};
and
class Item extends Component {
render() {
return (
<div onClick={() => this.props.onClick()}>item</div>
);
}
}
Item.propTypes = {
onClick: PropTypes.func
};
What is the proper way to get the DOM node of this.selectedItemRef in Items::scrollToItem()?
The React docs discourage the use of findDOMNode(), but is there any other way? Should I create the ref in Item instead? If so, how do I access the ref in Items::componentDidMount()?
Thanks
I think what you want is current e.g. this.selectedItemRef.current
It's documented on an example on this page:
https://reactjs.org/docs/refs-and-the-dom.html
And just to be safe I also tried it out on a js fiddle and it works as expected! https://jsfiddle.net/n5u2wwjg/195724/
If you want to get the DOM node for a React Component I think the preferred way of dealing with this is to get the child component to do the heavy lifting. So if you want to call focus on an input inside a component, for example, you’d get the component to set up the ref and call the method on the component, eg
this.myComponentRef.focusInput()
and then the componentRef would have a method called focusInput that then calls focus on the input.
If you don't want to do this then you can hack around using findDOMNode and I suppose that's why it's discouraged!
(Edited because I realized after answering you already knew about current and wanted to know about react components. Super sorry about that!)

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