Not able to render state using componentWillReceiveProps - reactjs

I am getting props from parent component and trying to render
From parent component, I am passing the headings
Parent Component:
class CoreCloudServices extends React.Component{
constructor(props){
super(props)
this.state = {
services:[]
}
}
loadData(){
var url = "https://api.myjson.com/bins/1ftfdx";
fetch(url)
.then(response => {
return response.json();
})
.then(d => {
this.setState({ services: d });
})
.catch(error => console.log(error))
}
componentDidMount() {
this.loadData();
}
render(){
<StatusFrame headings={this.state.services}/>
}
Child Component:
class StatusFrame extends React.Component{
constructor(props){
super(props)
this.state = {
labelHeading : this.props.headings
}
}
componentWillReceiveProps(newProps)
{
this.setState({labelHeading: newProps.headings} , ()=>{
console.log(this.state.labelHeading);
});
}
render(){
return(
<div>
<div>
{
this.state.labelHeading.map(((head, index) => {
<div>child {head.title}</div>
})
)
}
</div>
</div>
)}}
this.state.labelHeading is null but I am setting the state in componentwillreceiveprops()

you can just use the props without using the state , and you must return from your parent render method , also in map callback you should return too
class CoreCloudServices extends React.Component{
//...
render(){
return (<StatusFrame headings={this.state.services}/>)
}
}
class StatusFrame extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<div>
{
this.props.headings !== null ?
this.props.headings.map(( (head, index) =>
{
return <div>child {head.title}</div>
}))
:
null
}
</div>
</div>
)
}
}

Related

State doesn't update when props changes

i was learning react from 'React docs' for few days and today i got into the trouble. Link to docs: https://reactjs.org/docs/conditional-rendering.html#element-variables.
Exercise is that when the button clicks text will change. In docs, they did it with functions and it works perfectly but i tried it with classes.
The problem is that state doesn't update when props changes, it only have its initial value. I'm struggling with it since 2 hours and didn't find the solution. I'm new to React so please be forbearance.
Code:
class UserGreeting extends React.Component {
render() {
return (
<h1>Welcome back!</h1>
);
}
}
class GuestGreeting extends React.Component {
render() {
return (
<h1>Please sign up!</h1>
);
}
}
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: this.props.isLoggedIn};
}
render() {
let isLoggedIn = this.state.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
}
class LoginButton extends React.Component {
constructor(props) {
super(props);
this.state = {onClick: this.props.onClick};
}
render() {
return (
<button onClick={this.state.onClick}>Login</button>
);
}
}
class LogoutButton extends React.Component {
constructor(props) {
super(props);
this.state = {onClick: this.props.onClick};
}
render() {
return (
<button onClick={this.state.onClick}>Logout</button>
);
}
}
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: true};
}
handleLoginClick = () => {
this.setState({isLoggedIn: true});
}
handleLogoutClick = () => {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You are storing your answer in the state in the Greeting component inside the constructor. The constructor will only be called once at mount component.
Change your Greeting component with below code**
class Greeting extends React.Component {
render() {
let isLoggedIn = this.props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
}

Having trouble passing parent function to child component

Using info on StackOverflow, I've tried to pass a parent function to a child component via props; however, I keep running into errors when doing so. To simplify, here is my parent function:
class App extends Component {
constructor(props) {
super(props)
this.state = {
dailyPreloadMetrics: null,
queryResults: [0,0],
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
onClick(event) {
const value = event.target.value;
this.setState({isHidden: true});
fetch('http://localhost:5000/kw-finder', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({'firstParam': event.target.value,
'secondParam' : 300000})
}).then(response => {
console.log(response)
return response.json()
})
.then(json => {
console.log(json)
this.setState({queryResults: JSON.parse(json['query_results']),
dailyPreloadMetrics: JSON.parse(json['keyword_data']),
})
})
}
render() {
return (
<div className="App">
<QueryResultButtons data={this.state.queryResults}
onClick={this.onClick}
/>
</div>
)
}
}
export default App
My Child component is as follows:
class DynamicButton extends React.Component {
constructor(props) {
super(props)
this.state = {
secondParam: this.props.audienceMax
}
}
render() {
const {
data
} = this.props
const row = this.props.data && data.map((data) =>
<Button style={{margin: "10px"}}
value={data}
onClick={e => this.props.onClick(e)}
name='value'
>
</Button>
)
return (row)
}
}
class QueryResultButtons extends React.Component {
render() {
return (
<div>
<h1>Did you mean any of these instead?</h1>
<DynamicButton data={this.props.data && this.props.data}/>
</div>
)
}
}
export default QueryResultButtons
Basically I am trying to pass a function - onClick - from the parent to the two levels of child components (down to the DynamicButton Child Component). However, when I try this action, I get an error message stating: _this3.props.onClick is not a function. It seems the Child component isn't picking up the parent function but not sure what I'm doing wrong here...
When you insert the QueryResultButtons component you are passing the onClick function like onClick={this.onClick} but when you call the DynamicButton you are not passing the onClick function:
<DynamicButton data={this.props.data && this.props.data}/>
Try this:
<DynamicButton data={this.props.data && this.props.data} onClick={this.props.onClick}/>
The QueryResultButtons should be:
class QueryResultButtons extends React.Component {
render() {
return (
<div>
<h1>Did you mean any of these instead?</h1>
<DynamicButton data={this.props.data && this.props.data}
onClick={this.props.onClick}
/>
</div>
)
}
}
DynamicButton component is not direct child of App component, it is grandchild. So you have to pass onClick function from child component (QueryResultButtons) to grandchild component (DynamicButton) as well.
So, replace you QueryResultButtons with this:
class QueryResultButtons extends React.Component {
render() {
return (
<div>
<h1>Did you mean any of these instead?</h1>
<DynamicButton
data={this.props.data && this.props.data}
onClick={this.props.onClick}
/>
</div>
);
}
}

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

save react component and load later

I have react component in react native app and this will return Smth like this:
constructor(){
...
this.Comp1 = <Component1 ..... >
this.Comp2 = <Component2 ..... >
}
render(){
let Show = null
if(X) Show = this.Comp1
else Show = this.Comp1
return(
{X}
)
}
and both of my Components have an API request inside it ,
so my problem is when condition is changed and this toggle between Components , each time the Components sent a request to to that API to get same result ,
I wanna know how to save constructed Component which they wont send request each time
One of the ways do that is to handle the hide and show inside each of the child component comp1 and comp2
So you will still render both comp1 and comp2 from the parent component but you will pass a prop to each one of them to tell them if they need to show or hide inner content, if show then render the correct component content, else just render empty <Text></Text>
This means both child components exist in parent, and they never get removed, but you control which one should show its own content by the parent component.
So your data is fetched only once.
Check Working example in react js: https://codesandbox.io/s/84p302ryp9
If you checked the console log you will find that fetching is done once for comp1 and comp2.
Also check the same example in react native below:
class Parent extends Component {
constructor(props)
{
super(props);
this.state={
show1 : true //by default comp1 will show
}
}
toggleChild= ()=>{
this.setState({
show1 : !this.state.show1
});
}
render(){
return (
<View >
<Button onPress={this.toggleChild} title="Toggle Child" />
<Comp1 show={this.state.show1} />
<Comp2 show={!this.state.show1} />
</View>
)
}
}
Comp1:
class Comp1 extends Component
{
constructor(props) {
super(props);
this.state={
myData : ""
}
}
componentWillMount(){
console.log("fetching data comp1 once");
this.setState({
myData : "comp 1"
})
}
render(){
return (
this.props.show ? <Text>Actual implementation of Comp1</Text> : <Text></Text>
)
}
}
Comp2:
class Comp2 extends Component {
constructor(props) {
super(props);
this.state = {
myData2: ""
}
}
componentWillMount() {
console.log("fetching data in comp2 once");
this.setState({
myData2: "comp 2"
});
}
render() {
return (
this.props.show ? <Text>Actual implementation of Comp2</Text> : <Text></Text>
)
}
}
I think, you should move all your logic to the main component (fetching and saving data, so you component1 and component2 are simple dumb components. In component1 and component2 you can check "does component have some data?", if there isn't any data, you can trigger request for that data in parent component.
Full working example here: https://codesandbox.io/s/7m8qvwr760
class Articles extends React.Component {
componentDidMount() {
const { fetchData, data } = this.props;
if (data && data.length) return;
fetchData && fetchData();
}
render() {
const { data } = this.props;
return (
<div>
{data && data.map((item, key) => <div key={key}>{item.title}</div>)}
</div>
)
}
}
class App extends React.Component{
constructor(props){
super(props);
this.state = {
news: [],
articles: [],
isNews: false
}
}
fetchArticles = () => {
const self = this;
setTimeout( () => {
console.log('articles requested');
self.setState({
articles: [{title: 'article 1'}, {title: 'articles 2'}]
})
}, 1000)
}
fetchNews = () => {
const self = this;
setTimeout(() => {
console.log('news requested');
self.setState({
news: [{ title: 'news 1' }, { title: 'news 2' }]
})
}, 1000)
}
handleToggle = (e) => {
e.preventDefault();
this.setState({
isNews: !this.state.isNews
})
}
render(){
const { news, articles, isNews} = this.state;
return (
<div>
<a href="#" onClick={this.handleToggle}>Toggle</a>
{isNews? (
<News data={news} fetchData={this.fetchNews} />
): (
<Articles data={articles} fetchData={this.fetchArticles} />
)}
</div>
)
}
}

How to pass function as props in React?

I have functional component GetWeather which I want to pass result of GetLocation function as props based on which GetWetaher will do something i.e. another get request (in the example below it only renders its props). I think it has to happen inside ComponentDidMount, not sure how to do it
function GetLocation() {
axios.get('http://ipinfo.io')
.then((res) => {
return res.data.loc;
})
}
function GetWeather(props) {
//more code here, including another get request, based on props
return <h1>Location: {props.location}</h1>;
}
class LocalWeather extends Component {
componentDidMount() {
//???
}
render() {
return (
<div >
<GetWeather location={GetLocation}/> //???
</div>
);
}
}
Update: So based on suggestion from Damian below is working for me
function GetWeather(props) {
return <h3>Location: {props.location}</h3>;
}
class LocalWeather extends Component {
constructor(props) {
super(props);
this.state = {
location: []
};
}
getLocation() {
axios.get('http://ipinfo.io')
.then((res) => {
this.setState({location:res.data.loc});
})
}
componentDidMount() {
this.getLocation();
}
render() {
return (
<div >
<GetWeather location={this.state.location}/>
</div>
);
}
}
You can do it alternatively also
constructor() {
super();
this.state = {
Location:[]
}
}
function GetLocation() {
axios.get('http://ipinfo.io').then((res) => {
this.setState ({
Location:res.data.loc;
});
});
}
function GetWeather(props) {
return <h1>Location: {this.props.location}</h1>;
}
class LocalWeather extends Component {
componentDidMount() {
//code
}
render() {
return (
<div >
<GetWeather location={this.GetLocation.bind(this)}/>
</div>
);
}
}

Resources