I'm trying to get a picture from my folder with a path refer from my json and display it in FeaturedRooms.jsx.
...
src
components
FeaturedRooms.jsx
images
pages
Home.jsx
context.jsx
...
...
"images":[
{
"url":"../images/room-1.jpeg"
},
...
I'm using the context.jsx to handle and store the data.
const fetchUrl = async () => {
const resp = await fetch(url);
const respData = await resp.json();
const rooms = respData.rooms;
featured(rooms);
dispatch({ type: "ROOMS", payload: rooms });
};
useEffect(() => {
fetchUrl();
}, []);
const featured = (rooms) => {
let featuredRooms = rooms.filter((room) => room.room.featured === true);
dispatch({ type: "FEATURED_ROOMS", payload: featuredRooms });
};
And I'm trying to display it on the FeaturedRooms.jsx.
<div className="featured-wrapper">
{featuredRooms.map((rooms) => {
const { system, room } = rooms;
let images = room.images.map((image) => {
return image.url;
});
return (
<div className="room" key={system.id}>
<h2>{room.name}</h2>
<img src={images[0]} alt="featured-rooms" />
</div>
);
})}
</div>
But it's not working at all, is there a way for me to access it? And also, is there a way for me to make the images folder a static folder? Perhaps I will be accessing the images folder from a different folder.
edit: some more details.
Your folder structure is correct. What you need to do in your array is
...
"images":[
{
"url":require("../images/room-1.jpeg")
},
...
Then whenever you want to use this images array
images.map((item) =>(
<img src={item.url} />
))
Related
I'm using Strapi to call dynamic data into my website via an API GET request, and I want to generate paths for my dynamic pages. One level of dynamic pages works fine, but the second is a challenge.
My structure is as follows:
[category].js
[category]/[client].js
Both are dynamic, so I have, for example, a category "fashion" with multiple clients. The same goes for other categories like "products".
The first dynamic page works fine in building paths
[dynamic.js].
import CategoryCard from "../../../components/portfolio/categoryCard";
import { fetcher } from "../../../lib/api";
export const getStaticPaths = async () => {
const categoryPathResponse = await fetcher(
`${process.env.NEXT_PUBLIC_STRAPI_URL}/categories`
);
const data = categoryPathResponse.data;
const paths = data.map((path) => {
return {
params: { category: path.attributes.path.toString().toLowerCase() },
};
});
return {
paths,
fallback: false,
};
};
export async function getStaticProps(context) {
const category = context.params.category;
const categoryPropsResponse = await fetcher(
`${process.env.NEXT_PUBLIC_STRAPI_URL}/categories?filters[path][$eq]=${category}&?populate[0]=clients&populate[1]=clients.thumbnail`
);
return {
props: { category: categoryPropsResponse },
};
}
const CategoryOverviewPage = ({ category }) => {
const data = category.data;
const categoryTitle = data[0].attributes.Category;
return (
<>
{console.log('data for category before card', data)}
<div className="flex px-4 mt-24 lg:mt-12 lg:px-20">
<div>
<h1 className="[writing-mode:vertical-lr] [-webkit-writing-mode: vertical-lr] [-ms-writing-mode: vertical-lr] rotate-180 text-center">
{categoryTitle}
</h1>
</div>
<div className="grid grid-cols-[repeat(auto-fit,_minmax(150px,_250px))] gap-4 lg:gap-8 ml-4 lg:ml-32 max-w-[82vw]">
<CategoryCard data={data} />
</div>
</div>
</>
);
};
export default CategoryOverviewPage;
But the complexity comes with the second part, in which I have to create multiple paths per category. I tried and ended up with the following
[clients].js
export const getStaticPaths = async () => {
const categoryPathResponse = await fetcher(
`${process.env.NEXT_PUBLIC_STRAPI_URL}/categories?populate=*`
);
const data = categoryPathResponse.data;
const paths = data.map((path) => {
const category = path.attributes.path.toString().toLowerCase()
const client = path.attributes.clients.map((client) => client.name).toString().toLowerCase().replace(/\s+/g, "-")
return {
params: {
category: category, client: client
},
};
});
return {
paths,
fallback: false,
};
};
export async function getStaticProps(context) {
const category = context.params.category;
const client = context.params.client;
const data = await fetcher(
`${process.env.NEXT_PUBLIC_STRAPI_URL_BASE}/categories?filters[path][$eq]=${category}&?populate[clients][populate]=*&populate[clients][filters][name][$eq]=${client}`
);
return {
props: { client: data },
};
}
It seems to work for categories with only 1 item, which makes sense because a URL (path) is created like index/category/client.
But when there are multiple clients, it tries to create a path with 1 category and multiple clients attached to the same path, something like this category/client1client2.
This has to be separated, and for each client, there has to be a new path created like category1/client1, category1/client2, category2/client1, category2/client2, etc.
Any ideas?
In addition to mapping over the categories data, you also need to map over the clients array and generate a path entry for each.
Modify the code inside getStaticPaths in /[category]/[client].js as follows.
export const getStaticPaths = async () => {
// Existing code...
const paths = data.map((path) => {
const category = path.attributes.path.toString().toLowerCase()
return path.attributes.clients
.map((client) => {
const clientDetails = client.name.toLowerCase().replace(/\s+/g, "-")
return {
params: {
category: category, client: clientDetails
}
};
})
}).flat() // Flatten array to avoid nested arrays;
return {
paths,
fallback: false,
};
};
Trying to wrap my head around what the process would be to change an image source depending on the value of what is returned from the database.
Snippet:
function KnifesComponent() {
const knifeCollection = collection(db, "knives");
const [knives, setKnives] = useState([]);
useEffect(() => {
onSnapshot(knifeCollection, (snapshot) => {
setKnives(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
});
}, []);
return (
{knives.map((skin) => {
return (
{skin.rarity} // Returns "Ultra" for Example
<div><img src={isUltra : require('../../public/Rarity_Ultra.png')}></div>
);
}
I was thinking of making a const with all the values in and then using as needed but not sure how exactly to approach it
const rarities = {
Exclusive: require('../../public/Rarity_Exclusive.png'),
Ultra: require('../../public/Rarity_Ultra.png'),
}
What is the proper way to conditionally format different images based on values?
You can change the rarities object to this (paths as values):
const rarities = {
Exclusive: '../../public/Rarity_Exclusive.png',
Ultra: '../../public/Rarity_Ultra.png',
}
Then you can then access the image from the object by rarity.
function KnifesComponent() {
const knifeCollection = collection(db, "knives");
const [knives, setKnives] = useState([]);
useEffect(() => {
onSnapshot(knifeCollection, (snapshot) => {
setKnives(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
});
}, []);
return (
{
knives.map((skin) => {
return (
{skin.rarity} // Returns "Ultra" for Example
<div>
<img src={require(rarities[skin.rarity])}>
</div>
);
})
}
);
}
The object also can be like this (just the image names):
const rarities = {
Exclusive: 'Rarity_Exclusive.png',
Ultra: 'Rarity_Ultra.png',
}
And access them:
<img src={require(`../../public/${rarities[skin.rarity]}`)}>
I am trying to develop a Header that it takes logo image dynamically depends the client.
I have a JSON that stores the information, for example
{
"clientLogo": "assets/images/cLogo.jpg",
...
}
and I store it at a ContextProvider.
An example of code at my component:
const Header = () => {
const { clientLogo, clientName } = useClientContext();
const [clientLogoImage, setClientLogoImage] = useState(null);
useEffect(() => {
if (clientLogo) {
import(clientLogo)
.then((clientLogoImg) => {
setClientLogoImage(clientLogoImg);
})
.catch((err) => console.error(err));
}
}, [clientLogo]);
return (
<StyledHeader className="header">
<div className="header-container">
{clientLogoImage && (
<img
loading="lazy"
src={clientLogoImage}
className="logo"
alt={clientName}
/>
)}
</div>
....
I have already ensured that clientLogo = "assets/images/cLogo.jpg" and if I execute
useEffect(() => {
if (clientLogo) {
import("assets/images/cLogo.jpg")
.then((clientLogoImg) => {
setClientLogoImage(clientLogoImg);
})
.catch((err) => console.error(err));
}
}, [clientLogo]);
It works.
The problem is that when I use clientLogo variable I have this problem
Header.js:15 Error: Cannot find module 'assets/images/cLogo.jpg'
at |groupOptions: {}|namespace object:87:1
DO you have any idea?
Thank you in advance!
So, I have a new problem. I have tried to solve this a couple of days without success.
I'm trying to learn react and has creating a reactjs-site where I showcasing videos.
First I had problem to get the ID from the query string, but sort of solved it.
The code in my video.js looks like this:
function getDataFromDB(props) {
const [times, setTimes] = useState([])
const db = firebase.firestore();
useEffect(() => {
fire.firestore().collection('videoEvent').onSnapshot((snapshot) => { //time is the db name
const newTimes = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data()
}))
setTimes(newTimes)
})
}, [])
return times
}
const Video = (props) => {
const videoEvent = getDataFromDB()
const vidId = props.match.params.id;
console.log(vidId)
const { project } = props;
return (
<div className='div1080'>
<div className='flex-container'>
{videoEvent.map((videoEvent) =>
<div key={videoEvent.id} className='item' ><img className='imagevideo' src={photo} alt='hej' />
<br /> {videoEvent.Author} {videoEvent.Price}
{props.match.params.id}
<Link to={'video/' + videoEvent.id}>{videoEvent.eventTitle}</Link>
</div>
)}
</div>
</div>
)
}
export default Video
So now it is looping all the posts, that works fine, but I just want to show a specific post, with the ID from 'props.match.params.id'.
Any suggestions?
To access a single document for which you know the ID, see the documentation on getting a single document and getting realtime updates. Based on that:
fire.firestore().collection('videoEvent').doc(props.match.params.id).onSnapshot((doc) => {
const newTimes = [{
id: doc.id,
...doc.data()
])
setTimes(newTimes)
})
You'll need to pass the ID to getDataFromDB, which you seem to have forgotten:
const videoEvent = getDataFromDB(props)
What the below code does is to get data from API, and then render it on the page. searchChange function takes a value from the input tag, and setValue for query state. My api endpoint takes argument to filter the API such as http://127.0.0.1:8000/api/deals/?q=${query}.
I'm very confused how I can update the DealList component with the API updated with query state whenever typing something in the input tag. I'm thinking of that I need to something in searchChange function, but not sure what to do there.
index.js
const useFetch = (url, query, defaultResponse) => {
const [result, setResult] = useState(defaultResponse);
const getDataFromAPI = async url => {
try {
const data = await axios.get(url);
setResult({
isLoading: false,
data
});
} catch (e) {}
};
useEffect(() => {
if (query.length > 0) {
getDataFromAPI(`${url}?q=${query}`);
} else {
getDataFromAPI(url);
}
}, []);
return result;
};
const Index = ({ data }) => {
const query = useInput("");
const apiEndpoint = "http://127.0.0.1:8000/api/deals/";
const dealFetchResponse = useFetch(apiEndpoint, query, {
isLoading: true,
data: null
});
const searchChange = e => {
query.onChange(e);
query.setValue(e.target.value);
};
return (
<Layout>
<Head title="Home" />
<Navigation />
<Container>
<Headline>
<h1>The best lease deal finder</h1>
<h4>See all the lease deals here</h4>
</Headline>
<InputContainer>
<input value={query.value} onChange={searchChange} />
</InputContainer>
{!dealFetchResponse.data || dealFetchResponse.isLoading ? (
<Spinner />
) : (
<DealList dealList={dealFetchResponse.data.data.results} />
)}
</Container>
</Layout>
);
};
export default Index;
The biggest challenge in something like this is detecting when a user has stopped typing.. If someone is searching for 'Milk' - when do you actually fire off the API request? How do you know they aren't searching for 'Milk Duds'? (This is hypothetical, and to demonstrate the 'hard' part in search bars/APIs due to their async nature)..
This is typically solved by debouncing, which has been proven to work, but is not very solid.
In this example, you can search Github repos...but even in this example, there are unnecessary requests being sent - this is simply to be used as a demonstration. This example will need some fine tuning..
const GithubSearcher = () => {
const [repos, setRepos] = React.useState();
const getGithubRepo = q => {
fetch("https://api.github.com/search/repositories?q=" + q)
.then(res => {
return res.json();
})
.then(json => {
let formattedJson = json.items.map(itm => {
return itm.name;
})
setRepos(formattedJson);
});
}
const handleOnChange = event => {
let qry = event.target.value;
if(qry) {
setTimeout(() => {
getGithubRepo(qry);
}, 500);
} else {
setRepos("");
}
};
return (
<div>
<p>Search Github</p>
<input onChange={event => handleOnChange(event)} type="text" />
<pre>
{repos ? "Repo Names:" + JSON.stringify(repos, null, 2) : ""}
</pre>
</div>
);
};
ReactDOM.render(<GithubSearcher />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>