NextJS get custom JSON depending on page - json

I know you can do the following
export async function getStaticProps({ params }) {
console.log(params)
const res = await fetch(`https://example.com/api-access/news/2021_autumn_home_style_tips`)
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: { data }, // will be passed to the page component as props
}
}
however what if the last part depending on the news item a user presses needs to change.
https://example.com/api-access/news/2021_autumn_home_style_tips
https://example.com/api-access/news/2020_autumn_home_style_tips
https://example.com/api-access/news/2021_car
https://example.com/api-access/news/top_songs
How can I make a [slug].js page that allows me to run that slug url for example
https://myexample.com/news/top_songs
would fetch data from https://example.com/api-access/news/top_songs
I have tried
export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
console.log(params)
const res = await fetch('https://example.com/api-access/news/{slug}')
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: { data }, // will be passed to the page component as props
}
}
But get this error

Related

Cannot map results from API

I'm trying to dynamically generate routes in my next.js application. I have an api called getUsers that returns something like this:
{"users":[{"_id":"639a87ae8a128118cecae85b","username":"STCollier","image":"https://storage.googleapis.com/replit/images/1641322468533_db666b7453a6efdb886f0625aa9ea987.jpeg","admin":false,"likedPosts":["639e34c5991ecaea52ace9e4","639e34c7991ecaea52ace9e7","639e34c7991ecaea52ace9ea","639e39a216a642f686a28036","639e39a216a642f686a28037","639e3b3d8cdebd89d9691f97","639e3b3d8cdebd89d9691f98","639e3b3e8cdebd89d9691f9d","639e3b5a8cdebd89d9691fa0","639e3b5c8cdebd89d9691fa3","639e3b5c8cdebd89d9691fa6"],"dislikedPosts":[""]},{"_id":"639a88abc4274fba4e775cbe","username":"IcemasterEric","image":"https://storage.googleapis.com/replit/images/1646785533195_169db2a072ad275cfd18a9c2a9cd78a1.jpeg","admin":false,"likedPosts":[],"dislikedPosts":[]}
So I know the API works succesfully, but when trying to get these api results and generate a page for each username, I get an error stating:
TypeError: users.map is not a function
Here's my code for generating the routes:
//pages/user/[username].js
const Get = async (url) => {
return await fetch(url).then(r => r.json());
}
export async function getStaticPaths() {
const users = Get('/api/getUsers')
return {
paths: users.map(u => {
const username = u.users.username
return {
params: {
username
}
}
}),
fallback: false
}
}
What is wrong with my getStaticPaths() code? I know that the API is working, so why can't I map the results?
And if anyone needs the code for api/getUsers, here is that:
import clientPromise from "../../lib/mongodb";
import nc from "next-connect";
const app = nc()
app.get(async function getUsers(req, res) {
const client = await clientPromise;
const db = client.db("the-quotes-place");
let users = []
try {
const dbUsers = await db
.collection("users")
.find({})
.toArray();
users = dbUsers
return res.json({
users: JSON.parse(JSON.stringify(users)),
success: true
})
} catch(e) {
return res.json({
message: new Error(e).message,
success: false,
});
}
})
export default app
Thanks for any help!!
Modify Get method to return an async value instead of Promise.
As Get is an async method, you need the await in getStaticPaths method.
const Get = async (url) => {
let response = await fetch(url);
return await response.json();
}
export async function getStaticPaths() {
const users = await Get('/api/getUsers');
...
}

How could I pass JSON object array result to my api URL? (In REACT)

I have to fetch 2 api from backend, and try to get the result from this two. but, at the moment, the JSON result I get from the first API is object Array in JSON. I need to pass the id from first API(using setState) to second API for path variables. But when I do in my way, it fail to retrieve the data. Consider the code below:
componentDidMount(){
// console.log(loginEmail)
fetch(`http://localhost:9000/api/item/list`,)
.then((resp)=>{
resp.json().then((res)=>{
console.log(res.data);
// localStorage.setItem('id', res.data.user_info.id);
this.setState({data: res.data});
}
)
})
const id = this.state.data.id;
fetch(`http://localhost:9000/api/item/photo/view/${id}`,)
.then((resp)=>{
resp.json().then((res)=>{
console.log(res);
// localStorage.setItem('id', res.data.user_info.id);
this.setState({res});}
)
})
}
The problem is that fetch returns a Promise so, at the line
const id = this.state.data.id;
You do not have data populated yet.
You have to concatenate the two requests in a way like the following:
componentDidMount() {
fetch(`http://localhost:9000/api/item/list`)
.then((resp) => {
// return the id
})
.then((id) => {
fetch(`http://localhost:9000/api/item/photo/view/${id}`)
.then((resp) => {
// do what you need with the result
})
})
}
Fetch is asynchronous, which means javascript will
fetch data on the first call with no waiting, and continue
to the second fetch call where the id is not defined or Null.
In order to fix that you can use promises as follow
My code example
import React from "react";
class Home extends React.Component {
constructor() {
super();
this.state = {
res: [],
}
}
// http://jsonplaceholder.typicode.com/users
fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((resp) => {
resp.json().then((res) => {
console.log(res);
// localStorage.setItem('id', res.data.user_info.id);
resolve(res);
}
)
})
})
}
async componentDidMount() {
let data = await this.fetchData("http://jsonplaceholder.typicode.com/users");
console.log("data :", data);
let id = data[0].id;
console.log("Id :", id);
let newData = await this.fetchData(`http://jsonplaceholder.typicode.com/users/${id}`);
this.setState({ res: newData });
}
render() {
return (
<div>
Call API
</div>
)
}
}
export default Home
Adapted on your code
fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((resp) => {
resp.json().then((res) => {
console.log(res.data);
// localStorage.setItem('id', res.data.user_info.id);
resolve(res.data);
}
)
})
})
}
async componentDidMount() {
// console.log(loginEmail)
let data = await this.fetchData("http://localhost:9000/api/item/list");
let id = data.id;
let newData = await this.fetchData(`http://localhost:9000/api/item/photo/view/${id}`);
this.setState({ res: newData });
}
You need to make sure that each id gets its relevant results.
async componentDidMount() {
await fetch(`http://localhost:9000/api/item/list`)
.then(async (resp) => {
let req_ = resp.map((item)=>{
return await fetch(`http://localhost:9000/api/item/photo/view/${item.id}`)
})
let result = Promise.all(req_)
console.log(result)
})
}

Returning an array to React component from fetch call in Redux

I'm having trouble returning an array to my React component from a fetch call from my Express server, that I set up in Redux.
I'm trying to just return the vitamins array from this json from Express:
router.get('/', function(req, res, next) {
vitamins: [
{
name: "Vitamin B2"
}
],
minerals: [
{
name: "Zinc"
}
]});
});
This is the fetch call and FETCH_VITAMINS_SUCCESS action in my actions.js.
export function fetchVitamins() {
return dispatch => {
return fetch("/users")
.then(res => res.json())
.then(micros => {
dispatch(fetchVitaminsSuccess(micros.vitamins));
return micros.vitamins;
})
};
}
export const FETCH_VITAMINS_SUCCESS = 'FETCH_VITAMINS_SUCCESS';
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
This is my reducers.js where i'm trying to set the state to "micros.vitamins".
const initialState = {
micros: [],
};
function vitaminReducer(state = initialState, action) {
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state.vitamins,
micros: action.payload
};
default:
return state;
}
}
This is my React component Vitamins.js where I'm importing fetchVitamins() and trying to pass the names of each vitamins to a menu dropdown in an option tag.
componentDidMount() {
this.props.fetchVitamins();
}
renderData() {
const { vitamins } = this.state.micros;
return vitamins.map((micro, index) => {
return (
<option value={micro.value} key={index}>{micro.name}</option>
)
})
}
render() {
return (
<select value={this.props.value}>
{this.renderData()}
</select>
)
}
const mapStateToProps = state => ({
micros: state.vitamins,
});
export default connect(mapStateToProps, { fetchVitamins })(Vitamins);
Right now I get back the error "TypeError: Cannot read property 'micros' of null", highlighting over my renderData() function.
It should be:
const { vitamins } = this.props.micros;
because you pass micros as props from redux. While you tried to access it from the state (which I guess was not initialized that's why it's null).
Another thing, you pass Object in payload:
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
and set it as micros in your reducer:
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state.vitamins,
micros: action.payload
};
However, your micros are initially an array which may lead to unexpected errors. Maybe you should change your initial state to something resembling the response like:
const initialState = {
micros: {
vitamins: []
},
};
This way const { vitamins } = this.state.micros; will always return some array - before and after the response.

Bug in mapStateToProps() from fetching json object in Redux

I'm trying to fetch data from my Express server in Redux, and mapping over the object to just use one array, called "vitamins". This is the json object.
router.get('/', function(req, res, next) {
vitamins: [
{
name: "Vitamin B2"
}
],
minerals: [
{
name: "Zinc"
}
]});
});
This is my action.js, where I'm creating the function fetchVitamins() to just fetch micros.vitamins.
export function fetchVitamins() {
return dispatch => {
return fetch("/users")
.then(res => res.json())
.then(micros => {
dispatch(fetchVitaminsSuccess(micros.vitamins));
return micros.vitamins;
})
};
}
export const FETCH_VITAMINS_SUCCESS = 'FETCH_VITAMINS_SUCCESS';
export const fetchVitaminsSuccess = vitamins => ({
type: FETCH_VITAMINS_SUCCESS,
payload: { vitamins }
});
This is my reducers.js
const initialState = {
micros: [],
};
function vitaminReducer(state = initialState, action) {
switch(action.type) {
case FETCH_VITAMINS_SUCCESS:
return {
...state,
micros: action.payload.vitamins
};
default:
return state;
}
}
This is my React component Vitamins.js where I'm importing fetchVitamins() and trying to pass the names of each vitamins to a menu dropdown in an option tag.
componentDidMount() {
this.props.dispatch(fetchVitamins());
}
renderData() {
const { vitamins } = this.state.micros;
return vitamins.map((micro, index) => {
return (
<option value={micro.value} key={index}>{micro.name}</option>
)
})
}
render() {
return (
<select value={this.props.value}>
{this.renderData()}
</select>
)
}
const mapStateToProps = state => ({
micros: state.micros.vitamins,
});
Right now when it renders, I get this error: "TypeError: Cannot read property 'vitamins' of undefined", highlighting over "micros: state.micros.vitamins,".
Am I calling and setting state correctly? If I set my initialState to micros: [], then setting the state to "state.micros.vitamins" should work, I thought.
because of you get the server data n vitamins objects so that data should be in vitamins:[], in that Format so that why state.macros.vitamins work.

How to get data (of my api json) in my object ( Redux, React )?

I not undestand everything with javascript etc, I want to get my data returned by ma action redux but i'have a problem with my code.
const mapStateToProps = state => {
const group = state.groupReducer.group ? state.groupReducer.group : [ ]
return {
group
}
how i can get my data ?
When I try with that:
const mapStateToProps = state => {
const group = state.groupReducer.group.data.data[0] ? state.groupReducer.group.data.data[0] : [ ]
return {
group
}
And my goal is map around group
renderGroup = group => {
return group.map((groups => {
<div key={groups.data.data.id}>
//
</div>
}))
}
Sagas.js
export function* loadApiDataGroup() {
try {
// API
const response = yield
call(axios.get,'http://localhost:8000/api/group');
yield put(loadGroup(response))
} catch (e) {
console.log('REQUEST FAILED! Could not get group.')
console.log(e)
}
}
Action.js
export function loadGroup(data){ return { type: LOAD_GROUP, data }};
export function creatGroup(data){ return { type: CREATE_GROUP, data}};
// reducer
export default function groupReducer( state= {}, action = {}){
switch (action.type){
case LOAD_GROUP:
return {
...state,
group: action.data
}
case CREATE_GROUP:
return {
...state
}
default:
return state
}
thank you to help me
Try
const mapStateToProps = state => ({
group: state.groupReducer.group || []
});
Then you can use this.props.group in the component. Even though you might only want one thing in mapStateToProps, it's usually not directly returned like that.
If group is the response of an API request, you need to unpack data first, this is done in your async action creator (you will want to use redux-thunk or something similar):
const getGroup = () => async (dispatch) => {
dispatch({ type: 'GET_GROUP_REQUEST' });
try {
const { data } = await axios.get('/some/url');
dispatch({ type: 'GET_GROUP_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'GET_GROUP_FAILURE', payload: error });
}
};