React - Class component doesn't receive any params - reactjs

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

Related

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>
)
}

Calling props from a container

I am a little confused on the idea of using props in the context I am using for my React app. In my component, I need to check if the value of a certain prop (props.companyCode) matches a certain string, and only then will it print out a <p> of what I need. Below is what I have for calling the prop in the component:
Components/CompanyContact.jsx
class CompanyContact extends React.Component {
help() {
if (this.props.companyInfoList.companyCode === '1234') {
return <p>something</p>;
}
return <p>somethingelse</p>;
}
render() {
const help = this.help();
return (
<div>
{help};
</div>
)}}
export default CompanyContact;
And this is what I have for the container:
Container/InfoContainer.jsx
class InfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
companyInfoList: null,
};
}
async componentWillMount() {
const companyInfoCachedData = CachingService.getData('companyInfoList');
if (companyInfoCachedData) {
this.setState({ companyInfoList: companyInfoCachedData });
return;
}
}
async getCompanyInfo(accessToken) {
try {
const companyProfileResponse = await requestAWSGet('api/company-profile', undefined, accessToken);
CachingService.setData('companyInfoList', companyProfileResponse);
this.setState({ companyInfoList: companyProfileResponse });
} catch (err) {
throw err;
}
}
render() {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
}
}
export default InfoContainer;
Nothing is returned when I run the application and I believe it's because I'm not calling the prop correctly in my component but I am unsure as to how to go about fixing it. I'm fairly new to working with props so still trying to get my bearings.
I'm assuming you are getting an error somewhere because of this not having props and this.props.companyInfoList.companyCode trying to access a property on a non object. this.props.companyInfoList is initially set to null so accessing a property on it will break.
A few strategies to fix the problem:
Default it to an empty object
this.state = {
companyInfoList: {},
}
Block the rendering of the component until it has a value:
if (this.state.companyInfoList) {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
} else {
return null;
}
Check that the prop is an object and has the key companyCode on it:
if (this.props.companyInfoList &&
this.props.companyInfoList.companyCode &&
this.props.companyInfoList.companyCode === '1234') {
In addition, this will be in the wrong context and the changes above will most likely no be enough. Try changing to an arrow function like this:
help = () => {
// your code here
}
I would personally refactor that component logic and directly use the prop value inside the render method like:
class CompanyContact extends React.Component {
render() {
const { companyInfoList } = this.props;
return companyInfoList && companyInfoList.companyCode === '1234' ? (
<p>something</p>
) : (
<p>somethingelse</p>
)
}
}
export default CompanyContact;

Add component on button click

I am making a front end application using typescript and react. I have a component A which amongst other html elements has a textbox. I want to add this component A on click of a button. So if the user clicks the button multiple times, i want a new component A to be created on every click. Also I want to be able to store the text data so that I can later fetch it and process it.
I tried to make a list of this component but it gives me an error.
interface State {
componentList?: ComponentA[];
}
export class ComponentList extends React.Component<Props, State> {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }
public onClick(event) {
const componentList = this.state.componentList;
this.setState({
componentList: componentList.concat(<ComponentA key=
{componentList.length} />)
});
}
  public render() {
    return (
      <React.Fragment>
        <button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return ComponentA
})}
      </React.Fragment>
    );
  }
}
You might want to make two changes in your code.
First initialise your state in the constructor,
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = { componentList: [] }
}
So that react can track that data.
Second thing is, you are returning wrong item from the map in the render function.
Try returning component, which is different copies of <ComponentA ../> that you pushed every time you clicked the button,
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.state.componentList.map(function(component, index)
{
return component;
})}
</React.Fragment>
);
}
Keep the component count in the state:
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {
numComponents: 0
}
}
Add a new function which creates an array of the component for rendering later:
clickedComponents = () => {
let componentArray = [];
for (let i=0; i<this.state.numComponents; i++) {
componentArrays.push(<ComponentA />);
}
return componentArray;
}
Increment the component count with your onClick function:
public onClick(event) {
this.setState({numComponents: this.state.numComponents + 1});
}
Render the component array:
public render() {
return (
<React.Fragment>
<button onClick={this.onClick}>Add component</button>
{this.clickedComponents()}
</React.Fragment>
);
}

Passing value to props 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);
}

Call a function on application startup in react

I'm trying to call a function from application startup. The function reads data from JSON via dataVar (set elsewhere) and tries to load it into {items} for further consumption:
const dataVar = JSONStuff;
class Global extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
items: []
}
this.init();
}
// componentDidMount() {
// This doesn't work either!
// this.init();
// }
init() {
let { items } = dataVar;
this.setState({items});
}
render() {
return (
<div className="Global">
<Gallery items={this.state.items}/>
</div>
)
}
}
Then in Gallery.js:
import React, { Component } from 'react';
class Gallery extends Component {
render() {
return (
<div>
<h3>gallery:</h3>
{
this.props.items.map((item, index) => {
let {title} = item.name;
return (
<div key={index}>{title}</div>
)
})
}
</div>
)
}
}
export default Gallery;
Not sure why Global can't call a function inside of itself. I've tried with and without "this." I either get error to where the app won't complile or I get:
"Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op."
First of all, it's a warning, you probably better not call setState in componentDidMount.
My suggestion 1: assign value to state in constructor
constructor(props) {
super(props);
this.state = {
query: '',
items: dataVar.items,
};
}
Suggestion 2:
Do inside the componentWillReceiveProps
componentWillReceiveProps(nextProps) {
const { dataVar: items } = nextProps; // pass dataVar as props
this.setState({
items,
});
}
Plus try to debug your props and pay attention on your console for errors.

Resources