Reputation: 125
Edit: Solved using hooks
I've been going at this like crazy. I have a problem where I have to use a prop, in one other component. I understand that I should use contexts.
I've followed some tutorials, but for some reason, I cannot apply it to my own code.
export const FetchContinent = (props) => {
console.log(props.continent)
return (
<div>
{props.continent}
</div>
)
};
props.continent logs exactly what I want.
I want to use that exact value in this other component, as a prop.
const JumbotronPage = (continent) => {
return (
<MDBContainer className="test">
<MDBRow>
<MDBCol>
<MDBJumbotron className="text-center">
<MDBCardTitle className="card-title h4 pb-2">
<CountryList
continent={["NA"]} //<-- HERE!!
displayFields={["countryName"]}
/>
<CountryList continent={["NA"]} displayFields={["revName"]} />
</MDBCardTitle>
So I want to use "props.continent" like (note that this is in another JS-file and another component):
<CountryList
continent={[props.continent]} //<-- HERE!!
displayFields={["countryName"]}
/>
I feel like I've turned every stone but I cannot come up with a solution. I've vacuumed my failed tries to make the code more readable.
Edit: Updated code: FetchContinent:
export const FetchContext = createContext();
export const FetchContinent = (props) => (
<FetchContext.Provider value={props}>
{console.log("Fetch continent: " + props.continent)}
{props.children}
</FetchContext.Provider>
);
I'd like to print that value in another file:
const JumbotronPage = (props) => {
console.log("Props.continent: " + props.continent);
const value = useContext(FetchContext);
return (
<FetchContext.Consumer>
<h1>{value}</h1>
</FetchContext.Consumer>
);
};
However, Value becomes undefined.
Edit 2: This is how I use FetchContinent:
const Africa = ({}) => {
return (
<div>
<VerContinentToolbar before="Memories" continent={["Africa"]} />
<FetchContinent continent={["Africa"]} />
Edit 3: Full code: JUMBOTRON (Code I had before I started with contexts):
const JumbotronPage = (props) => {
console.log(<VerContinentToolbar props={props.props} />);
console.log("fdfds");
return (
<MDBContainer className="test">
<MDBRow>
<MDBCol>
<MDBJumbotron className="text-center">
<MDBCardTitle className="card-title h4 pb-2">
<CountryList continent="Africa" displayFields={["countryName"]} />
<CountryList continent="Africa" displayFields={["revName"]} />
<CountryList
continent={props.continent}
displayFields={["countryName"]}
/>
<CountryList
continent={props.continent}
displayFields={["revName"]}
/>
</MDBCardTitle>
<MDBCardImage
src="https://mdbootstrap.com/img/Photos/Slides/img%20(70).jpg"
src="https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX32059289.jpg"
className="img-fluid"
/>
<MDBCardBody>
hej
<MDBCardTitle className="indigo-text h5 m-4">
<CountryList continent="Africa" displayFields={["beerPrice"]} />
<CountryList continent="Africa" displayFields={["foodPrice"]} />
<CountryList
continent="Africa"
continent={props.continent}
displayFields={["beerPrice"]}
/>
<CountryList
continent={props.continent}
displayFields={["foodPrice"]}
/>
<CountryList
continent={props.continent}
displayFields={["hostelPrice"]}
/>
<CountryList continent="Africa" displayFields={["dest1"]} />
<CountryList continent="Africa" displayFields={["dest2"]} />
<CountryList continent="Africa" displayFields={["dest3"]} />
<CountryList
continent={props.continent}
displayFields={["dest1"]}
/>
<CountryList
continent={props.continent}
displayFields={["dest2"]}
/>
<CountryList
continent={props.continent}
displayFields={["dest3"]}
/>
</MDBCardTitle>
<MDBCardText>
<CountryList continent="Africa" displayFields={["review"]} />
I want:
<CountryList
continent={props.continent}
displayFields={["dest3"]}
/>
props.continent to be passed from:
const Africa = ({}) => {
return (
<div>
<VerContinentToolbar before="Memories" continent={["Africa"]} />
<Breadcrumb continent="Africa" />
This component.
Edit 4: So, if I access my component "Africa": https://i.sstatic.net/IwmhO.jpg I'd like to load every country from my database with the continent "Africa"
When I click "Africa", I want to see every country currently in the database associated with Africa: https://i.sstatic.net/4vvnh.jpg
When I click a country, I want to display the information from the database in a jumbotron: https://i.sstatic.net/Khxrv.jpg
The data I'd like to view: https://i.sstatic.net/TwhNa.jpg
Problem is, When I click on a country, I have to set the continent to the corresponding continent. First, I wanted to pass it as a prop, but then I got recommended to use context and now I'm here.
Edit 5: I'm not really sure if contexts is the way to go. A friend recommended me to try it but could not help me further.
Edit 6: Country-list:
import React, { Component, useState, useEffect } from 'react'
import { Link } from 'react-router-dom';
import firebase from '../config'
import './Countries.css'
const useCountries = continent => {
const [countries, setCountries] = useState([]);
console.log("Country list: " + continent)
useEffect(() => {
firebase
.firestore()
.collection(continent[0])
.onSnapshot((snapshot) => {
const newCountries = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data()
}))
setCountries(newCountries)
})
}, [])
return countries
}
const CountryList = ({ continent, displayFields = [] }) => {
const countries = useCountries(continent); // <--- Pass it in here
return (
<div className="countries">
{countries.map(country => (
<div key={country.id}>
<div className="entry">
{displayFields.includes("continent") && (
<div>Name of continent: {country.continent}</div>
)}
{displayFields.includes("revName") && (
<div>{country.revName}</div>
)}
{displayFields.includes("countryName") && (
<div><Link to={"./Jumbotron"}>{country.countryName}</Link></div>
)}
{displayFields.includes("dest1") && (
<div>Destination 1: {country.dest1}</div>
)}
{displayFields.includes("dest2") && (
<div>Destination 2: {country.dest2}</div>
)}
{displayFields.includes("dest3") && (
<div>Destination 3: {country.dest3}</div>
)}
{displayFields.includes("beerPrice") && (
<div>Beer price: {country.beerPrice}</div>
)}
{displayFields.includes("foodPrice") && (
<div>Food price: {country.foodPrice}</div>
)}
{displayFields.includes("hostelPrice") && (
<div>Hostel price: {country.hostelPrice}</div>
)}
{displayFields.includes("review") && <div>Review: {country.review}</div>}
{displayFields.includes("imgUrl") && <img src={country.url} alt="no-img" />}
</div>
</div>
))}
</div>
);
};
export default CountryList
Thanks,
Upvotes: 1
Views: 694
Reputation: 2803
There is library which actually makes it easier to use multiple contexts https://www.npmjs.com/package/react-dynamic-context-provider.
Upvotes: 0
Reputation: 2948
Assuming that you read the docs about context API and some other sources. I will try to cover some missing parts I had while working with context for my first times.
Context
consists of two parts: A single provider, and consumers. The provider is the part responsible on holding the values, and the consumers will get these values and will be notified with every update.const MyContext = React.createContext(null);
// bellow code is for illustration only
const MyContextProdiver = ({ children }) => <MyContext.Provider value={{ continentsData: [] }}>{children}</MyContext.Provider>;
const MyContextConsumer = () => {
const contextValue = React.useContext(MyContext);
return null; // a react element must be returned here
}
<MyContextProvider>
<MyAppThatSomewhereHasAConsumer />
// or may be directly <MyContextConsumer /> but the idea of the context is to be used in multiple parts
</MyContextProvider>
If you have only one consumer, you probably don't need the context and you must locate your state in the right place.
While using Context api across several projects, I use a trick that I find a good practice to notify me if I forgot to put the provider in the right place (also to notify my teammates). I create a hook to use my context, ie: useMyContext()
// this assumes that your context will always start with a truthy value
function useMyContext() {
const myContextValue = React.useContext(MyContext);
invariant(myContextValue, 'Have you forgot to put a <MyContext.Provider> somewhere up in the tree ?');
// or simply put if (!myContextValue) throw new Error('there is no MyContext provider')
return myContextValue;
}
// later
const contextValue = useMyContext(); // and you will notified if you are in a tree where there is no provider
Projecting these notes into your requirements, I suggest that:
ContinentProvider
that will wrap all consumers and provide them with the countries.useContinentCountries
that uses the useContinentProvider
Here is a codesandbox demo that looks like this:
Upvotes: 1
Reputation: 41
React Context needs a Provider of the Data and a Consumer.
https://de.reactjs.org/docs/context.html#contextprovider
https://de.reactjs.org/docs/context.html#contextconsumer
Upvotes: 0