0
votes

I'm a beginner in React and stuck with some problem. I have two components. one is parent(App.js) which passes its state to child component(CardList). the other is child which receives passed state and use it as props. What I wanted to do is when I click the search button and give some username it will search data of the relevant username and I'll append to the state array defined in parent functional component and in child, I sent it as props and In child component I'll iterate over the list of props.stateArray and print the corresponding data. Even I have put the console.log() to check whether the flow goes to this child component or not but this console runs only when 1st render calls after that every time when the state change the console won't log any message.Please find the image below:-enter image description here




Parent Component **App.js**
import React, { useState } from 'react';
import Form from './Form';
import CardList from './CardList';

export default function App() {
  const [profiles, setProfiles] = useState([]);
  const addNewProfile=(profile)=>{
      profiles.push(profile);
      setProfiles(profiles);
  
     console.log(profiles);
  }
  return (
    <>
  
      <Form addProfile={addNewProfile}/>
      <CardList profilese={profiles}/>
    </>
  );
}


Child Component -- **CardList.js**
import React from 'react';
import Cardi from './Cardi';

export default function CardList(props)
{

   console.log("Everytime State of Parent change");
     return(
      <div>
        {props.profilese.map(profile => <Cardi key={profile.id} {...profile}/>)}
      </div>
       );
}


    Another Child Component -- **Form.js**
import React,{useState} from 'react';
import axios from 'axios';
import regeneratorRuntime from "regenerator-runtime";

export default function Form(props)
{
    const[username,setUsername]=useState("");

   const handleSubmit=async(event)=>{
         event.preventDefault();
         try{
        const resp= await axios.get
         (`https://api.github.com/users/${username}`);
         const data=await resp.data;
          props.addProfile(data);
         }catch(err)
         {
             console.log("Exception Occured"+ err);
         }
    }
    return(
     <form  onSubmit={handleSubmit}>
         <input type="text"
         value={username}
         onChange={event=>
            {setUsername(event.target.value)}}
     
         />
         <button >Search</button>
     </form>
    );
}


**Cardi.js**
import React from 'react';


export default function Cardi(props)
{

console.log(props.profile);
if(props.profile===undefined)
{
    return (
       <div>Can't find that username</div>
    );
}
else{
return (
  
  
    <>
       <img src={props.profile.avatar_url}/>
       <div className="info">
      <div className="name">{props.profile.name}</div>
      <div className="company">{props.profile.company}</div>
    </div>
    </>
  
);
}
}

enter image description here

3

3 Answers

1
votes

try this instead:

const addNewProfile=(profile)=>{
      setProfiles([...profiles, profile]);
}

your state is an array, is reference based. when you do profiles.push(profile) you are not changing its reference. so when you setProfiles(profiles) you are passing the same reference. react wont do deep match to compare. fwiw, avoid mutating directly the state.

update

about the extra questions:

1 - setState is async, meaning your console.log wont print the state updated since state has not updated yet. you may want to use the console.log statement along useEffect from hooks, which triggers on Mount and Update lifecycles. or you can use react devtools.

2 - you get undefined, because you are acessing wrong your props at Cardi:

{props.profilese.map(profile => <Cardi key={profile.id} {...profile}/>)}

above you are passing profile properties not profile object itself. but at cardi you try to access profile object, which is not what you have. you can change it to profile = {profile}, or remove from cardi all the profile chainings and access each variable directly like props.name

0
votes

In order to re-render the child everytime the state changes in the parent, you can maintain a state variable in the child and update it everytime the value of props changes(componentDidUpdate would work). Now rather than directly outputting the props...output the state variable.

Will edit this to add a demo/code later. Hope this helps.

0
votes

App.js

 import React, { useState } from 'react';
import Form from './Form';
import CardList from './CardList';

export default function App() {
  const [profiles, setProfiles] = useState([]);
  const addNewProfile=(profile)=>{
    setProfiles([...profiles, profile]);
    console.log(profiles);
}
  return (
    <>

      <Form addProfile={addNewProfile}/>
      <CardList profilese={profiles}/>
    </>
  );
}

@buzatto I have changed the code but when I call the api for the first time it won't add to the profiles array and when I call the api for the 2nd time after that it would add the result of the 1st hit of an api to the profiles state array.

2nd concern is I'm getting undefined value of profile props that I'm passing from Parent component(CardList.js) to Child Component(Cardi.js).

Please refer to the screenshot below:-

enter image description here

Another question:- As I have logged the value of profiles props in CardList it gives the updated value of state that I have defined in the Parent Component(App.js) and whereas state itself defined in the parent still not gets updated. Please refer to the screenshot below:- enter image description here