Passing data from child component - reactjs

const Location = (props) => {
const [province, setProvince] = useState(null);
const [discrict, setDistrict] = useState(null);
const [discrictList, setdiscrictList] = useState([]);
const handleProvinceChange = (obj, e) => {
setProvince(obj);
setdiscrictList(obj.ilce);
setDistrict(null);
};
const handleDistrictChange = (obj) => {
setDistrict(obj);
};
return (
<div style={{ width: 400, marginBottom: 20 }}>
<Select
placeholder="Şehri Seçiniz"
value={province}
options={il}
onChange={handleProvinceChange}
getOptionLabel={x => x.il}
getOptionValue={x => x.il}
/>
<Select
placeholder="İlçeyi Seçiniz"
value={discrict}
options={discrictList}
onChange={handleDistrictChange}
getOptionLabel={x => x.ilce}
getOptionValue={x => x.il}
/>
</div>
)
}
export default Location;
I wanna send discrict data from this code to this component to use a function from here. Location component is rendered in List Component,List Component is in Content.
class Content extends React.Component {
state = {
locations: [],
allLocations: [],
query: "",
};
componentDidMount() {
console.log("DATA");
/*I wanna pass this data to use this function*/ LocationsAPI.getLocations().then(resp =>
this.setState({ locations: resp, allLocations: resp })
);
}
render() {
console.log(this.state.locations);
return (
<div className="content">
<List/>
</div>
);
}
}
export default Content;
I've searched it too much. But it always gives errors. How can i pass discrict data for using Content component?

My understanding is that you want to pass district data from the Location component to it's ancestor the Content Component. Furthermore, you want the function that uses that data to run on Component Mount. I think one way of doing this would be to simply define a method inside of the Content Component that accepts the data as an argument. Then you can just pass that function down to the Location Component using the List Component. Since you have access to the function in the Location component, you can call it using your data. Since ComponentDidMount runs after all children have rendered, we can simply use a useEffect hook in the Location Component, and call our function just based on that hook.
It might look something like this:
class Content extends React.Component {
state = {
locations: [],
allLocations: [],
query: "",
};
LocationFunction(data){
console.log("DATA");
/*I wanna pass this data to use this function*/
LocationsAPI.getLocations().then(resp =>
this.setState({ locations: resp, allLocations: resp }));
}
render() {
console.log(this.state.locations);
return (
<div className="content">
<List LocationFunction={LocationFunction}/>
</div>
);
}
}
export default Content;
const Location = (props) => {
const [province, setProvince] = useState(null);
const [discrict, setDistrict] = useState(null);
const [discrictList, setdiscrictList] = useState([]);
useEffect(()=>{
props.LocationFunction(data);
},[district]);
const handleProvinceChange = (obj, e) => {
setProvince(obj);
setdiscrictList(obj.ilce);
setDistrict(null);
};
const handleDistrictChange = (obj) => {
setDistrict(obj);
};
return (
<div style={{ width: 400, marginBottom: 20 }}>
<Select
placeholder="Şehri Seçiniz"
value={province}
options={il}
onChange={handleProvinceChange}
getOptionLabel={x => x.il}
getOptionValue={x => x.il}
/>
<Select
placeholder="İlçeyi Seçiniz"
value={discrict}
options={discrictList}
onChange={handleDistrictChange}
getOptionLabel={x => x.ilce}
getOptionValue={x => x.il}
/>
</div>
)
}
export default Location;

Related

React useState async setter doesn't update value passed as props

I have this component in my React project -
const ViewPost = (props: Props) => {
const [listingData, setListingData] = useState<any>({})
const [auctionData, setAuctionData] = useState<any>({})
useEffect(() => {
if (props.listingId) {
getListingData()
}
}, [props.listingId])
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
setListingData(data?.data)
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
const auctions = async (auctionId: any) => {
const auction = await getAuctions(auctionId)
console.log('auction', auction.data)
setAuctionData(auction.data)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}
export default ViewPost
Basically, I'm getting data from an API and assigning it to auctionData.
console.log(auction.data) shows me the desired result but when I pass auctionData as props into Details I get an empty object which leads to a lot of issues, since useState is async.
How can I overcome this problem?
const [auctionData, setAuctionData] = useState<any>({})
your default value is an empty object, that causes the problems.
should set null or undefined as default value, and hide the Details when not have the data.
Use loading state. Once data is fully fetched from api then pass to child component. I think what is happeing here is that child component is called with empty state variable while data is still being fetched.
const [isLoading, setIsLoading] = useState(true)
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
.then((data) => {setListingData(data)})
.then((data) => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
})
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
and then return
if (isLoading) {
return (
<div>
Loading...
</div>
)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}

Can a ReactContext with 2 instances be created and used in 2 different React component?

I Have created a context and have Default values say
{ isopen = true, searchValue = 'abc'}
Now i want to use this context with in 2 different components with different values.
Say componentA can have React context value
{ isopen = true, searchValue = 'xyz'}
Say componentB can have React context value
{ isopen = true, searchValue = 'uvw'}
without both interfering with each other.
You can't do it currently (v17) out of the box, but there are common workarounds.
You can have either a single object to separate the provider's value for each component, or you can have separate providers.
It doesn't matter which you choose (depends on your use-case), I recommend having separate providers (readability).
Full example:
const InputContext = React.createContext();
const InputContext2 = React.createContext();
const Consumer = () => {
const { value } = React.useContext(InputContext);
return <>{value}</>;
};
const Consumer2 = () => {
const { value2: value } = React.useContext(InputContext);
return <>{value}</>;
};
const Consumer3 = () => {
const { value } = React.useContext(InputContext2);
return <>{value}</>;
};
const App = () => {
return (
<>
<InputContext.Provider value={{ value: "xyz", value2: "uvw" }}>
<h2>
Consumer1: <Consumer />
</h2>
<h2>
Consumer2: <Consumer2 />
</h2>
</InputContext.Provider>
<hr />
<InputContext.Provider value={{ value: "xyz" }}>
<InputContext2.Provider value={{ value: "uvw" }}>
<h2>
Consumer1: <Consumer />
</h2>
<h2>
Consumer3: <Consumer3 />
</h2>
</InputContext2.Provider>
</InputContext.Provider>
</>
);
};

React: save ref to state in a custom hook

I want to create a ref to an element, save it in state and use it somewhere else, down the line. Here is what I have so far:
const Header = () => {
const topElement = useRef();
const { setRootElement } = useScrollToTop();
useEffect(() => {
setRootElement(topElement);
}, []);
return (
<div ref={topElement}>
...
</div>
)
}
The useScrollToTop hook:
export const useScrollToTop = () => {
const [rootElement, setRootElement] = useState();
const scrollToTop = () => {
rootElement.current.scrollIntoView();
};
return {
scrollToTop: scrollToTop,
setRootElement: setRootElement
};
};
And in a different component:
const LongList = () => {
const { scrollToTop } = useScrollToTop();
return (
<div>
....
<button onClick={() => scrollToTop()} />
</div>
);
}
The setRootElemet works okay, it saves the element that I pass to it but when I call scrollToTop() the element is undefined. What am I missing here?
As hooks are essentially just functions, there is no state shared between calls. Each time you call useScrollToTop you are getting a new object with its own scrollToTop and setRootElement. When you call useScrollToTop in LongList, the returned setRootElement is never used and therefore that instance rootElement will never have a value.
What you need to do is have one call to useScrollToTop and pass the returned items to their respective components. Also, instead of using a state in the hook for the element, you can use a ref directly and return it.
Putting these together, assuming you have an App structure something like:
App
Header
LongList
Hook:
export const useScrollToTop = () => {
const rootElement = useRef();
const scrollToTop = () => {
rootElement.current.scrollIntoView();
};
return {
scrollToTop,
rootElement,
};
};
App:
...
const { scrollToTop, rootElement } = useScrollToTop();
return (
...
<Header rootElementRef={rootElement} />
<LongList scrollToTop={scrollToTop} />
...
);
Header:
const Header = ({ rootElementRef }) => {
return (
<div ref={rootElementRef}>
...
</div>
);
}
LongList:
const LongList = ({ scrollToTop }) => {
return (
<div>
...
<button onClick={() => scrollToTop()} />
</div>
);
}
The issue probably is topElement would be null initially and useEffect would trigger setRootElement with null. You would need to keep topElement in state variable and check when it changes and set the value inside your JSX as
const [topElement, setTopElement] = useState(null);
useEffect(() => {topElement && setRootElement(topElement);}, [topElement])
return (
<div ref={(ref) => setTopElement(ref)}>
...
</div>
);

React hooks useEffect calls mutiple times when redux store other data changed

my code like this:
Info component:
import {
getAttachData,
} from '#src/actions/creators/account'
const Info: React.FC = () => {
const info = useSelector<any, Account>(state => state.getIn(['account', 'info']).toJS())
const list = useSelector<any, Data[]>(state => state.getIn(['account', 'list']).toJS())
const attach = useSelector<any, AttachData[]>(state => state.getIn(['account', 'attach']).toJS())
...
const handleChange = ({ select }) => {
dispatch(getAttachData({v: select}))
}
const Template = (params) => {
return (
<div>
<BaseSelect onChange={(val) => handleChange(val)} list={list} />}
</div>
)
}
return (
...
<Template data={info} />
{attach.map((child, cidx) => (<Template data={child} />))}
)
}
export default Info
BaseSelect component:
const BaseSelect: React.FC<Props> = props => {
const [selectId, setSelectId] = useState('')
const { list } = props
useEffect(() => {
if (!isEmpty(list)) {
...
}
console.log('init')
}, [])
const handleChange = (value) => {
setSelectId(value)
props.onChange({
select: value,
})
}
return (
<Select
data={list}
value={selectId}
onChange={handleChange}
/>
)
}
export default BaseSelect
when excute handleChange event in BaseSelect component, the props.onChange function will call handleChange event in info component, and dispatch http request getAttachData which will change attach data in redux store, but useEffect in BaseSelect component will also excute and in console will print 'init' two times.
console:
It's because your Template component re-creates every time when redux store is changing.
Just move Template component outside the Info component.

Map of an array in a functional component and access to a specific object

function TypeArticleOne(props) {
let apiData = props.apiData;
const [ therapists, setTherapists ] = useState(apiData.topProfiles.therapists);
const [speciality, setSpeciality]= useState('ABA');
const [pageLoading, setPageLoading]= useState(true);
const topProfilesUrl = 'therapists/top/profiles'
useEffect(() => {
console.log(speciality);
getTopTherapists();
window.scrollTo(0, 0);
}, []);
const getTopTherapists = () => {
setPageLoading(true);
loadTopTherapists();
};
const loadTopTherapists = () => {
console.log("second");
props.actions.reqGetTherapistsTopProfiles({
body: {},
headers: null,
resource: `${topProfilesUrl}`
})
};
useEffect(() => {
if (apiData.topProfiles && apiData.topProfiles.success) {
const therapistsLoad = apiData.topProfiles.therapists;
setPageLoading(false);
setTherapists([therapists].concat(therapistsLoad));
}
}, []);
How to map an array in a functional component? I want to map the therapists array from the functional component above. React is suggesting me to use UseRef(), because I have a functional component and I am using hooks, but it's not clear for me.
I call the therapists in an array from an database and I need to map them to render in a card, inside a functional component. For now I can access the array elements, but I need to access some specific parameters of the objects. Could you help me guys?
const renderTherapists = (props) => {
const items = props.therapists.map( (t, idx) => (
<TherapistCard therapist={t} key={idx} />
))
return (
<div ref={0} className="therapist-list">
{ items }
</div>
)
}
Rather than declaring a const, it is better to directly map the props like this:
const RenderTherapists = props => {
return (
<div className="therapist-list">
{ props.therapists.map((t, idx) => {
return <TherapistCard therapist={t} key={idx} />
})}
</div>
)
}
export default RenderTherapists;

Resources