I have created a nextjs page and inside the page, there is getServerSideProps that calls API to fetch data.
The problem is that getServerSideProps never calls API and returns a response error 403 Unauthorized
nextjs is serving data from .next folder and returns a JSON response and instead of calling API it calls this URL http://localhost:3000/_next/data/development/contacts.json
This is my code
import React, { useState, useEffect } from "react"
import {
Card,
CardHeader,
CardFooter,
Container,
Row,
Button
} from "reactstrap"
import AdminLayout from "../components/layouts/AdminLayout"
import PropTypes from "prop-types"
import TablePagination from "../components/common/TablePagination"
import { fetchData } from "../api"
import { toast } from "react-toastify"
import { useSession } from "../context/SessionContext"
import ProtectPage from "../hocs/ProtectPage"
import { getContacts } from "../api/contacts"
import ContactsTable from "../components/contacts/ContactsTable"
const Contacts = ({ initialContacts, initialPagination, error }) => {
const [contacts, setContacts] = useState(initialContacts)
const [pagination, setPagination] = useState(initialPagination)
const session = useSession()
const handlePagination = async (page) => {
fetchData({
apiMethod: () => getContacts({ page }),
callback: (res, error) => {
if (error || !res) {
toast("Error loading Contacts")
} else {
setContacts(res.data)
setPagination(res.pagination)
}
session.loadingQueue.dequeue()
}
})
}
useEffect(() => {
setContacts(initialContacts)
setPagination(initialPagination)
}, [initialContacts, initialPagination])
return (
<AdminLayout>
{/* Page content */}
<Container className="mt--7" fluid>
<Row>
<div className="col">
<Card className="shadow">
<CardHeader className="border-0">
<Row className="justify-content-between w-100 m-0">
<h3 className="mb-0">Contacts</h3>
</Row>
</CardHeader>
<ContactsTable contacts={contacts ?? []} />
<CardFooter className="py-4">
<TablePagination
{...pagination}
handlePagination={handlePagination}
/>
</CardFooter>
</Card>
</div>
</Row>
</Container>
</AdminLayout>
)
}
Contacts.propTypes = {
initialContacts: PropTypes.array.isRequired,
initialPagination: PropTypes.object.isRequired
}
export async function getServerSideProps(ctx) {
try {
const contactsRes = await getContacts({})
const { data: initialContacts, pagination: initialPagination } =
contactsRes.data
return {
props: {
initialContacts,
initialPagination
}
}
} catch (error) {
return {
props: {
initialContacts: [],
initialPagination: {},
error: true
}
}
}
}
export default ProtectPage(Contacts)
I tried to disable cache and nothing worked. still serving JSON files.
console.log(contactsRes) in getServerSideProps
Or
call your api from getServerSideProps (await fetch(your_path))
Related
I was trying to display an array of data fetched from my custom server with RTK Query using Next.js (React framework). And this is my first time using RTK Query. Whenever I console.log the data, it appears in the browser console. But whenever I try to map the data to render it in the browser, it keeps throwing an error saying Cannot read properties of undefined (reading 'map').
I figured Next.js always throws an error if an initial state is undefined or null even if the state change. This link talked about solving the problem using useMemo hook https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics
But I didn't understand it well. Please kindly help me out with displaying the data.
Here is the BaseQuery function example I followed, it was derived from redux toolkit docmentation https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#axios-basequery
import axios from "axios";
const axiosBaseQuery =
({ baseUrl } = { baseUrl: "" }) =>
async ({ url, method, data }) => {
try {
const result = await axios({ url: baseUrl + url, method, data });
return { data: result.data };
} catch (axiosError) {
let err = axiosError;
return {
error: { status: err.response?.status, data: err.response?.data },
};
}
};
export default axiosBaseQuery;
I make the GET request here
import { createApi } from "#reduxjs/toolkit/query/react";
import axiosBaseQuery from "./axiosBaseQuery";
export const getAllCarsApi = createApi({
reducerPath: "getAllCarsApi",
baseQuery: axiosBaseQuery({
baseUrl: "http://localhost:5000/",
}),
endpoints(build) {
return {
getAllCars: build.query({
query: () => ({ url: "all-cars", method: "get" }),
}),
};
},
});
export const { useGetAllCarsQuery } = getAllCarsApi;
This is my redux store
import { configureStore } from "#reduxjs/toolkit";
import { getAllCarsApi } from "./getAllCarsApi";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
const store = configureStore({
reducer: { [getAllCarsApi.reducerPath]: getAllCarsApi.reducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(getAllCarsApi.middleware),
});
setupListeners(store.dispatch);
export default store;
I provide the store to the _app.js file.
import "../styles/globals.css";
import axios from "axios";
import { MyContextProvider } from "#/store/MyContext";
import { Provider } from "react-redux";
import store from "#/store/ReduxStore/index";
axios.defaults.withCredentials = true;
function MyApp({ Component, pageProps }) {
return (
<MyContextProvider>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</MyContextProvider>
);
}
export default MyApp;
I get the data here in my frontend.
import { useGetAllCarsQuery } from "#/store/ReduxStore/getAllCarsApi";
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
export default theTest;
This is a timing thing.
Your component will always render immediately and it will not defer rendering until data is there. That means it will also render before your data has been fetched. So while the data is still loading, data is undefined - and you try to map over that.
You could do things like just checking if data is there to deal with that:
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data && data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
This is the error message I am getting
TypeError: undefined is not an object (evaluating 'drinkList.drinks[0]')
How can I fix the error so that I can use the app to fetch data from the external api?
This is my drink.js code:
import React, { useEffect, useState } from "react";
import axios from "axios";
import Drinks from "./Drinks";
function Home() {
const [drinkName, setDrinkName]= useState([]);
const [drinkList, setDrinkList] = useState([]);
const drinksURL = `https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${drinkName}`;
const handleChangeDrink= e => {
setDrinkName(e.target.value);
}
const getDrink = () => {
axios
.get(drinksURL)
.then(function (response) {
setDrinkList(response.data);
console.log(drinksURL);
})
.catch(function (error) {
console.warn(error);
});
};
return (
<main className="App">
<section className="drinks-section">
<input
type="text"
placeholder="Name of drink (e.g. margarita)"
onChange={handleChangeDrink}
/>
<button onClick={getDrink}>Get a Drink Recipe</button>
<Drinks drinkList={drinkList} />
</section>
</main>
);
}
export default Home;
This is my Drink.js code:
import React from "react";
function Drinks({ drinkList }) {
if (!drinkList) return <></>;
return (
<section className="drinkCard">
<h1>{drinkList.drinks[0].strDrink}</h1>
</section>
);
}
export default Drinks;
Couple of problems here...
drinkName is initialised as an array but it appears to be expecting a string
drinkList is initialised as an array but the data from the API is an object. You may want to assign the drinks array from the response instead
drinksUrl is never updated
An empty array is still truthy
Some easy fixes
const [drinkName, setDrinkName]= useState(null) // initialise as null
const [drinkList, setDrinkList] = useState([])
// don't include the "s" parameter here
const drinksURL = "https://www.thecocktaildb.com/api/json/v1/1/search.php"
const getDrink = () => {
// pass drinkName as a param
axios.get(drinksURL, {
params: { s: drinkName }
}).then(res => {
// note that we're extracting `drinks`
setDrinkList(res.data.drinks)
}).catch(console.warn)
}
and in Drink.js
function Drinks({ drinkList }) {
// check the array length
return drinkList.length && (
<section className="drinkCard">
<h1>{drinkList[0].strDrink}</h1>
</section>
)
}
I am trying to display my stripe component, but I am getting this error:
IntegrationError: Only one element of type cardNumber can be created.
I don't know why, since I'm only using it once in my entire app
Any ideas?
This is my index
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import MyComponent from './components/StripeComponent';
const promise = loadStripe("xxx-xxx-xxx");
ReactDOM.render(
<React.StrictMode>
<Elements stripe={promise}>
<MyComponent/>
</Elements>
</React.StrictMode>,
document.getElementById('root')
);
And this is my component
import React from "react";
import {
useElements,
} from "#stripe/react-stripe-js";
const MyComponent: React.FC= (props)=>{
const elements = useElements();
const cardNumberElement = elements?.create('cardNumber', {
placeholder: ''
});
const cardExpiryElement = elements?.create('cardExpiry', {
placeholder: ''
});
const cardCvvElement = elements?.create('cardCvc', {
placeholder: ''
});
cardNumberElement?.mount('#card-number-element')
cardExpiryElement?.mount('#card-expiry-element')
cardCvvElement?.mount('#card-cvv-element')
const handleSubmit = async (ev: any) => {
ev.preventDefault();
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<div id="card-number-element"></div>
<div id="card-expiry-element"></div>
<div id="card-cvv-element"></div>
</form>
);
}
export default MyComponent
Seems it is because you create and mount the card components in the functional component body they are executed on every render of the component, i.e. as an inadvertent side-effect.
Place the creation and mounting logic in an useEffect hook with an empty dependency array so that it is invoked only once when the component mounts.
import React, { useEffect } from "react";
import { useElements } from "#stripe/react-stripe-js";
const MyComponent: React.FC = (props) => {
const elements = useElements();
// Effect hook to run once on component mount
useEffect(() => {
const cardNumberElement = elements?.create("cardNumber", {
placeholder: ""
});
const cardExpiryElement = elements?.create("cardExpiry", {
placeholder: ""
});
const cardCvvElement = elements?.create("cardCvc", {
placeholder: ""
});
cardNumberElement?.mount("#card-number-element");
cardExpiryElement?.mount("#card-expiry-element");
cardCvvElement?.mount("#card-cvv-element");
}, []); // <-- empty dependency array
const handleSubmit = async (ev: any) => {
ev.preventDefault();
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<div id="card-number-element"></div>
<div id="card-expiry-element"></div>
<div id="card-cvv-element"></div>
</form>
);
};
useEffect(() => {
if (elements) {
const cardNumberElement =
elements.getElement("cardNumber") || // check if we already created element
elements.create("cardNumber", defaultInputStyles); // create if dont
cardNumberElement.mount("#numberInput");
}
}, [elements]);
I had the same problem, in my case, I had a reference to CardNumberElement in another section of my code. After removing it, everything worked fine.
I can't make my prop render in HTML. I'm making an app for a Christian ministry and I want to be able to post like a blog, I got quill working but I can't show the results rendered, is showing pure HTML.
I'v been trying to follow the rules of react-render-html, but my experience is little, so I don't really know what I'm missing. I try use 'renderHTML' but it doesn't work.
Below is my code, and if you see the screenshot, you will see that the first card is showing the HTML tags.
import React from 'react';
import { Container, Card, Button, CardTitle, CardText, CardColumns, CardSubtitle, CardBody, Collapse } from 'reactstrap';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { connect } from 'react-redux';
import { getPosts, deletePost } from '../actions/postActions';
import PropTypes from 'prop-types';
import axios from 'axios';
import renderHTML from 'react-render-html';
import PostsForm from './extentions/PostsForm';
class Home extends React.Component {
componentDidMount() {
this.props.getPosts();
}
onDeleteClick = (id) => {
this.props.deletePost(id);
}
constructor(props) {
super(props);
this.onEntering = this.onEntering.bind(this);
this.onEntered = this.onEntered.bind(this);
this.onExiting = this.onExiting.bind(this);
this.onExited = this.onExited.bind(this);
this.toggle = this.toggle.bind(this);
this.state = {
collapse: false,
status: 'Closed',
ButtonText: "Submit Post"
};
}
onEntering() {
this.setState({ status: 'Opening...' });
}
onEntered() {
this.setState({ status: 'Opened' });
}
onExiting() {
this.setState({ status: 'Closing...' });
}
onExited() {
this.setState({ status: 'Closed', ButtonText: "Submit Post" });
}
toggle() {
this.setState(state => ({ collapse: !state.collapse, ButtonText: "Close" }));
}
formOpening = () => {
this.setState({
on: !this.state.on
})
}
render(){
const { posts } = this.props.post;
return(
<Container>
<div style={{float: "left"}}><h5>Current state: {this.state.status}</h5></div>
<div style={{float: "right"}}><Button
color="dark"
style={{marginButtom: '2rem'}}
onClick={this.toggle}>{this.state.ButtonText}</Button></div>
<Collapse
isOpen={this.state.collapse}
onEntering={this.onEntering}
onEntered={this.onEntered}
onExiting={this.onExiting}
onExited={this.onExited}
style={{clear: "both"}}
>
<Card>
<CardBody>
<PostsForm />
</CardBody>
</Card>
</Collapse>
<CardColumns style={{clear: "both"}}>
<TransitionGroup className="Posts">
{posts.map(({ _id, title, subtitle, postbody}) => (
<CSSTransition key={_id} timeout={500} classNames="fade">
<Card>
<CardBody>
<Button className="remove-btn" color="danger" size="sm" onClick={this.onDeleteClick.bind(this, _id)}>×</Button>
<CardTitle><h3>{title}</h3></CardTitle>
<CardSubtitle><h4>{subtitle}</h4></CardSubtitle>
<CardText>{postbody}</CardText>
<Button>Read More</Button>
</CardBody>
</Card>
</CSSTransition>
))}
</TransitionGroup>
</CardColumns>
</Container>
)
}
};
Home.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts, deletePost })(Home);
Screenshot of how it looks now
I would like to see that the cards are acting like
Body Text ect etc etc not <p>Body Text ect etc etc</p>
You need to use dangerouslySetInnerHTML API.
From React Docs, slightly modified:
function createMarkup(html) {
return {__html: html};
}
function MyComponent({html}) {
return <div dangerouslySetInnerHTML={createMarkup(html)} />;
}
https://reactjs.org/docs/dom-elements.html
I get data from a local server, catch them with axios.get, and save them in my state. It's ok, but when i want to pass it as props in an component child, KABOOM! Doesn't work.
I'm looking for a solution, I think it's lyfecycle problem but i'm not sure.
App.js
import React, { Component } from 'react';
import './style/App.css';
import axios from 'axios'
import Table from './Components/Table'
class App extends Component {
state = {
tabData: [],
}
componentWillMount = () => {
this.getDataFromServer()
}
getDataFromServer = () => {
axios.get("http://localhost:8000")
.then((response) => {
const twentyObj = response.data.splice(-20);
this.setState({
tabData:twentyObj
})
console.log(this.state.tabData)
})
.catch(function (error) {
console.log(error);
})
}
render() {
return (
<div className="App">
<Table stateData={this.state.tabData}/>
</div>
);
}
}
export default App;
Developer Tools Browser say:
TypeError: _this.props is undefined
(for this.props.tabData.map in Table.js)
Table.js
import React from 'react';
import Cell from './Cell'
const Table = (props) => {
return(
<div>
{this.props.tabData.map( item =>
<Cell key={item.index}
time={item.timestamp}
nasdaq={item.stocks.NASDAQ}
cac40={item.stocks.CAC40}/>
)}
</div>
)
}
export default Table;
Table is functional component this has no value there and thats what the error message is telling you.
You should use props.tabData and not this.props.tabData
UPDATE:
Here you are passing it as stateData and not tabData Try props.stateData