getServerSideProps or getStaticProps is not getting called if I use the component as markup in nextjs - javascript

I just started with the NextJS. I have created two pages PageA and PageB. Here is the code
PageB
import type { NextPage } from 'next';
import axios from 'axios';
const PageB: NextPage = ({ data = [] }: any) => {
return (
<div>
<table style={{ width: "100%" }}>
<thead>
<tr style={{ width: "100%" }}>
<th style={{ width: "20%" }}>Name</th>
</tr>
</thead>
{/* rows */}
<tbody>
{
data.map((post: any) => {
return <tr key={post._id}>
<td>{post.name}</td>
</tr>;
})
}
</tbody>
</table>
</div>
)
}
export const getServerSideProps = async ({ query }) => {
const myData = await axios.get(`http://localhost:443/api/data`);
return { data: myData.data };
}
export default PageB;
and PageA that will use PageB
import type { NextPage } from 'next';
import axios from 'axios';
import PageB from '../pageb';
const PageA: NextPage = ({ chats }: any) => {
return (
<div>
<table style={{ width: "100%" }}>
<thead>
<tr style={{ width: "100%" }}>
<th style={{ width: "50%" }}>Sender Id</th>
</tr>
</thead>
{/* rows */}
<tbody>
{
chats.map((chat: any) => {
return <tr key={chat._id}>
<td>{chat.sender_id}</td>
</tr>;
})
}
</tbody>
</table>
</div>
<PageB></PageB>
</>
)
}
export const getServerSideProps = async ({ query }) => {
const chats = await axios.get(`http://localhost:443/api/chats`);
// console.log(posts);
return { chats: chats.data };
}
export default PageA;
Now PageB shows the empty list. And if i directly redirect to the /pageb it shows the data.
what is the problem?

Related

how to make InfiniteScroll implementation updated because it update only one time in React table?

import React, { useEffect, useMemo, useState } from 'react' ;
import { useTable , useSortBy } from 'react-table';
import MOCK_DATA from '../MOCK_DATA.json';
import { Columns } from './columns';
import {Checkbox} from './Checkbox';
import InfiniteScroll from 'react-infinite-scroll-component';
export default function DefaultTable() {
const columns = useMemo(() => Columns , [])
const data = useMemo(() => MOCK_DATA , [])
const [items , setItems] = useState(data);
const [hasMore , setHasMore] = useState(true)
const [offset, setOffset] = useState(10);
const fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in .5 secs
if (items.length >= items.length +1) {
setHasMore( false );
return;
}
setTimeout(() => {
setOffset( offset + 10);
}, 100);
};
const {getTableProps ,
getTableBodyProps ,
headerGroups ,
rows ,
prepareRow,
allColumns,
getToggleHideAllColumnsProps
} = useTable ({
columns ,
data
},
useSortBy
)
return (
<div>
<div>
<Checkbox {...getToggleHideAllColumnsProps()} /> Toggle All
{
allColumns.map(column =>(
<span key={column.id}>
<label>
<input type='checkbox' {...column.getToggleHiddenProps()} />
{column.Header}
</label>
</span>
))
}
</div>
<div
id="scrollableDiv"
style={{
height: 320,
overflow: 'auto',
display: 'flex',
}}
>
<InfiniteScroll
dataLength={rows.length} //This is important field to render the next data
next={fetchMoreData}
hasMore={hasMore}
style={{ display: 'flex', flexDirection: 'column' }}
loader={<h4>Loading more items...</h4>}
scrollableTarget="scrollableDiv"
endMessage={
<p style={{ textAlign: 'center' }}>
<b>Yay! You have seen it all</b>
</p>
}
>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render('Header')}
<span>
{column.isSorted
? column.isSortedDesc
? ' 🔽'
: ' 🔼'
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.slice(0, offset).map((row) => {
// {rows.map((row) => {
prepareRow(row)
return(
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
</InfiniteScroll>
</div>
</div>
)
}

React Bootstrap Tabs - Keep Current Tab On Page Reload

I'm DELETING data from my database via an API call, then returning data from my database via another API call, but the 'Car' tab doesn't seem to update or refresh showing the new data.
I want to refresh the tab page via STATE or via a page reload and keep the user's selected tab using React Bootstrap, either:
reload just the TAB the user is in
RELOAD the whole page whilst keeping the TAB selected
React Bootstrap Tabs
<Tabs defaultActiveKey="profile" id="uncontrolled-tab-example" className="mb-3">
<Tab eventKey="bus" title="Bus">
<Bus />
</Tab>
<Tab eventKey="car" title="Car">
<Car />
</Tab>
<Tab eventKey="van" title="Van">
<Van />
</Tab>
</Tabs>;
myFile.js:
const Car = () => {
const [car, setCar] = useState([{ '': '' }]);
const [selectedCar, setSelectedCar] = useState('');
useEffect(() => {
async function fetchData() {
try {
const data = await getCompanyCar(car.id);
setCar(data)
console.log(data);
} catch (err) {}
}
fetchData();
}, []);
return (
<>
<CarModal car={selectedCar} onCancel={() => setSelectedCar(null)} />
<Table className="p-4 rounded-3" striped>
<thead className="bold fs-5" style={{ color: 'rgb(80, 80, 80)' }}>
<tr style={{ display: 'flex', justifyContent: 'space-between' }}>
<span className="text-nowrap" style={{ width: '0px' }}>
Car
</span>
<button className="addButton" onClick={null}>
Add
</button>
</tr>
</thead>
<tbody>
{/* TODO:PAGINATE ROWS */}
{car.map((car) => (
<React.Fragment key={car.client_id}>
<CarRow car={car} setSelectedCar={setSelectedCar} />
</React.Fragment>
))}
</tbody>
</Table>
</>
);
};
function CarFunction({ car, setSelectedCar }) {
const [showConfirmResetModal, setShowConfirmResetModal] = useState(false);
const handleDelete = async (event) => {
try {
const data = await deleteCompanyCar(Car.id);
if (data[0].completed === 'deleted') {
getCompanyCar(Car);
window.location.reload();
}
} catch (err) {
console.log(err);
alert('Error, unable to delete');
}
};
return (
<ConfirmationModal
header="Confirm Deletion"
show={showConfirmResetModal}
onHide={() => setShowConfirmResetModal(false)}
onConfirm={handleDelete}
annimation
>
Are you sure you want to delete this car?
</ConfirmationModal>
);
}
export default CarFunction;
Thanks in advance!

Passing Props (img url ) using React-Modal (react js)

I need to pass image url to modal in react js. Like, on click the item from the "imgae attachment", it shows the modal with image for selected item. But it can't show my image data by passing img={item.document}, Here is my code below:
DepositRecord.js
import React, { Component } from "react";
import { Table } from "react-bootstrap";
import { Button, ButtonToolbar } from "react-bootstrap";
import { AddDepositModal } from "./AddDepositModal";
export class DepositRecord extends Component {
constructor(props) {
super(props);
this.state = { deps: [], addModalShow: false };
}
componentDidMount() {
this.refershList();
}
refershList() {
this.setState({
deps: [
{ id: 9, userId: "12", document: "img1_url" },
{ id: 8, userId: "16", document: "img2_url" },
{ id: 6, userId: "13", document: "img3_url" },
{ id: 4, userId: "1", document: "img4_url" },
{ id: 2, userId: "1", document: "img5_url" }
]
});
}
render() {
const { deps } = this.state;
let addModalClose = () => this.setState({ addModalShow: false });
return (
<div>
<h3>Customer's Deposit Record</h3>
<br />
<Table className="mt-4" striped bordered hover size="sm">
<thead>
<tr>
<th>Deposit id</th>
<th>user id</th>
<th>img attachment</th>
</tr>
</thead>
<tbody>
{deps.map((item) => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.userId}</td>
<td>
<ButtonToolbar>
<Button
variant="primary"
onClick={() => this.setState({ addModalShow: true })}
>
image attachment
</Button>
<AddDepositModel
show={this.state.addModalShow}
onHide={addModalClose}
img={item.document}
/>
</ButtonToolbar>
</td>
</tr>
))}
</tbody>
</Table>
</div>
);
}
}
export default DepositRecord;
AddDepositModal.js <--the Madal component
import React, { Component } from 'react';
import { Modal, Button, Row, Col, Form } from 'react-bootstrap';
export class AddDepositModal extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Deposit Record
</Modal.Title>
</Modal.Header>
<Modal.Body>
<img src={this.props.img} width={700} height={1100}/>
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
}
export default AddDepositModal;
My Problem: I am not able to pass the image URL to a Modal component and have no better idea solving it in this code.
Please help me and if any including, changes or complete solution for perfect understanding for the requirement would be really great. Many Thanks in Advance!
Hello here's your solution
DepositRecord.js
import React, { useEffect, useState } from "react";
import { Button, ButtonToolbar, Table } from "react-bootstrap";
import AddDepositModal from "./AddDeposiModal";
const DepositRecord = () => {
const [deps, setDeps] = useState([]);
const [visibleModal, setVisibleModal] = useState(false);
const [depImage, setDepImage] = useState(null);
useEffect(() => {
loadDepsHandler();
}, []);
const loadDepsHandler = () => {
const myRequest = new Request("https://randomuser.me/api/", {
method: "GET",
cache: "default",
});
debugger;
fetch(myRequest)
.then((res) => res.json())
.then((data) => {
const { results } = data;
setDeps(results);
})
.catch((err) => console.log(err));
};
const setDepHandler = (id) => {
const dep = deps.find((a) => a.id.value === id);
debugger;
setDepImage(dep.picture.large);
setVisibleModal(true);
};
return (
<div>
<h3>Customer's Deposit Record</h3>
<br />
<Table className="mt-4" striped bordered hover size="sm">
<thead>
<tr>
<th>Deposit id</th>
<th>user name</th>
<th>img attachment</th>
</tr>
</thead>
<tbody>
{deps.map((item) => (
<tr key={item.id.name}>
<td>{item.id.name}</td>
<td>{item.value}</td>
<td>
<ButtonToolbar>
<Button
variant="primary"
onClick={() => setDepHandler(item.id.value)}
>
image attachment
</Button>
</ButtonToolbar>
</td>
</tr>
))}
</tbody>
</Table>
{visibleModal && (
<AddDepositModal
show={visibleModal}
onHide={() => setVisibleModal(false)}
image={depImage}
/>
)}
</div>
);
};
export default DepositRecord;
AddDepositModal.js
import React from "react";
import { Button, Modal } from "react-bootstrap";
const AddDepositModal = ({ show, onHide, image }) => {
return (
<Modal show={show} onHide={onHide}>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Deposit Record
</Modal.Title>
</Modal.Header>
<Modal.Body>
<img src={image} width={700} height={1100} alt={image} />
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
);
};
export default AddDepositModal;
Async call added. This API is public so it's will take some time to get results
.

React siblings components communication

I have an upload file component, delete file component and files table component(presents the existing files in the system using axios) in the same page:
filesPage.js
import React from 'react';
import UploadFile from '../components/UploadFile'
import DeleteFile from '../components/DeleteFile'
import FilesTable from '../components/FilesTable'
function UploadFiles() {
return (
<div className="filesPage">
<UploadFile/>
<DeleteFile/>
<FilesTable/>
</div>
)
}
export default UploadFiles;
Now I want every time I upload new file or delete one, the files table will be updated which means after the axios post/delete, I need to rerender the files table component and do axios get again to get the active files.
Someone can help?
FilesTable.js
import React, {useState, useEffect} from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TablePagination from '#material-ui/core/TablePagination';
import TableRow from '#material-ui/core/TableRow';
import axios from 'axios';
function FilesTable() {
const [tfaArray, setTfaArray] = useState([]);
useEffect(() => {
axios.get("api/tfa").then((res) => setTfaArray(res.data)).catch((err) => console.log(err));
}, [])
const columns = [
{id: 'fileId', label: '#', minWidth: 100},
{id: 'name', label: 'name', minWidth: 100},
{id: 'date', label: 'upload date', minWidth: 100}
];
const rows = tfaArray.map((tfa, index) => ({fileId: (index + 1), name: tfa.fileName, date: tfa.dateTime.slice(0,24)}) )
const useStyles = makeStyles({
root: {
width: '50%',
position: 'absolute',
right: '10%',
top: '15%',
},
container: {
maxHeight: 322
},
headerCell: {
background: '#F5F5F5',
fontSize: '16px',
zIndex: '0'
}
});
const classes = useStyles();
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
return (
<>
<Paper className={classes.root}>
<TableContainer className={classes.container}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell className={classes.headerCell}
key={column.id}
style={{ minWidth: column.minWidth }}>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row, index) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={index}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id}>
{value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10, 15]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
</>
)
}
export default FilesTable;
Typically with React you do this by lifting state up as described in this React documentation.
In this case, you'd lift the state to the parent of these components, and have the FilesTable receive the list of files as a prop. (Props are basically component state managed by the parent rather than by the component itself.) Similarly the DeleteFile component would receive the function to call to delete a file, the UploadFile component would receive the function to use to add a file, etc.
Here's a simplified example:
const {useState} = React;
const Parent = () => {
const [files, setFiles] = useState([]);
const addFile = (file) => {
setFiles(files => [...files, file]);
};
const removeFile = (file) => {
setFiles(files => files.filter(f => f !== file));
};
return (
<div>
<FilesTable files={files} removeFile={removeFile} />
<UploadFile addFile={addFile} />
</div>
);
};
const FilesTable = ({files, removeFile}) => {
return (
<React.Fragment>
<div>{files.length === 1 ? "One file:" : `${files.length} files:`}</div>
<ul className="files-table">
{files.map(file => (
<li>
<span>{file}</span>
<span className="remove-file" onClick={() => removeFile(file)}>[X]</span>
</li>
))}
</ul>
</React.Fragment>
);
};
const UploadFile = ({addFile}) => {
const [file, setFile] = useState("");
const onClick = () => {
addFile(file);
setFile("");
};
return (
<div>
<input type="text" value={file} onChange={(e) => setFile(e.target.value)} />
<input type="button" value="Add" disabled={!file} onClick={onClick} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
ul.files-table {
list-style-type: none;
}
.remove-file {
margin-left: 0.5rem;
cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

React JS proper way to set a loader UI

I was trying to replicate my local project within Code Sandbox and I found my mistake. What I am trying to do in these projects is show a loader spinner while fetching some data from a Express + GraphQL server, but instead, the loader won't hide when the data is loaded.
This is the code where I fetch the data:
import React, { useEffect, useContext, useState } from "react";
import GlobalContext from "../../context/Global/context";
import gql from "graphql-tag";
import { useLazyQuery } from "#apollo/react-hooks";
const GET_USERS = gql`
query {
users {
id
name
address {
street
}
}
}
`;
export default props => {
const globalContext = useContext(GlobalContext);
const [getUsers, { called, loading, data: users }] = useLazyQuery(GET_USERS);
useEffect(() => {
getUsers();
}, []);
useEffect(() => {
console.log(globalContext.loading)
if (globalContext.loading && users.length) {
globalContext.setLoading(false);
}
}, [globalContext, users]);
if (called && loading) {
globalContext.setLoading(true);
}
if (!users) {
return <p>There are no users</p>;
}
console.log(users)
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Street</th>
</tr>
</thead>
<tbody>
{users.map(user => {
return (
<tr>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.address.street}</td>
</tr>
);
})}
</tbody>
</table>
);
};
And this is the code where I set up the Loader:
import React, { useContext, useEffect } from "react";
import { ApolloClient } from "apollo-boost";
import { ApolloProvider } from "#apollo/react-hooks";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import Users from "./components/Users";
import GlobalContext from "./context/Global/context";
import Loader from "react-loader-spinner";
const cache = new InMemoryCache();
const link = new HttpLink({
uri: "https://73rcp.sse.codesandbox.io/"
});
const client = new ApolloClient({
link,
cache
});
export default () => {
const globalContext = useContext(GlobalContext);
if (globalContext.loading) {
return (
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Loader type="Puff" />
</div>
);
}
return (
<ApolloProvider client={client}>
<Users />
</ApolloProvider>
);
};
This is the frontend code
This is the backend code
I always have the same issue, not sure what I am doing wrong, but I guess it's because when I set globalContext.setLoading(true) the component rerenders and the first component App.js loads before than Users component.
If this is the error or not, what is the proper way to set a loader spinner while fetching any data from anywhere? Thanks in advance.
So after some debugging, the main problem was that Users component was unmounting before receiving the data from the server. Also, I encountered some naming conflicts.
// App.js
export default () => {
const globalContext = useContext(GlobalContext);
// This is making the Users component unmount which won't allow to change the
// global loading state once the data is received, hence the perpetual loading.
if (globalContext.loading) {
return (
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Loader type="Puff" />
</div>
);
}
return (
<ApolloProvider client={client}>
<Users />
</ApolloProvider>
);
};
A simple fix would be to render both the Loader and the Users component, however, the former will be positioned absolutely so the user can't interact with the Users component.
export default () => {
const globalContext = useContext(GlobalContext);
return (
<ApolloProvider client={client}>
<Users />
{globalContext.loading && (
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
position: "absolute",
top: 0,
backgroundColor: "white",
justifyContent: "center",
alignItems: "center"
}}
>
<Loader type="Puff" />
</div>
)}
</ApolloProvider>
);
};
On the Users component there were some issues:
// Users component
export default props => {
const globalContext = useContext(GlobalContext);
// data: users will not return users object, instead it is only renaming it.
// if we were to destructure it like data: { users }, an error would be thrown since
// data.users is undefined.
const [getUsers, { called, loading, data: users }] = useLazyQuery(GET_USERS);
useEffect(() => {
getUsers();
}, [getUsers]);
useEffect(() => {
// data does not contain a length property since it is an object. Again an
// error would be thrown.
if (globalContext.loading && users.length) {
globalContext.setLoading(false);
}
}, [globalContext, users]);
if (called && loading) {
globalContext.setLoading(true);
}
if (!users) {
return <p>There are no users</p>;
}
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Street</th>
</tr>
</thead>
<tbody>
{/* Since data is not an array, map would be undefined (error again)*/}
{users.map(user => {
return (
<tr>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.address.street}</td>
</tr>
);
})}
</tbody>
</table>
);
};
So to fix this we have:
export default props => {
const globalContext = useContext(GlobalContext);
const [getUsers, { called, loading, data }] = useLazyQuery(GET_USERS);
useEffect(() => {
getUsers();
}, [getUsers]);
useEffect(() => {
if (globalContext.loading && data) {
globalContext.setLoading(false);
}
}, [globalContext, data]);
if (called && loading) {
globalContext.setLoading(true);
}
if (!data) {
return <p>There are no users</p>;
}
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Street</th>
</tr>
</thead>
<tbody>
{data &&
data.users.map(user => {
return (
<tr>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.address.street}</td>
</tr>
);
})}
</tbody>
</table>
);
};
All changes are in this sandbox.
There are multiple ways to implement loader in react app while getting data or sending some data from/to server.
The best way is to create a higher order component that will load the wrapping feature component or a loader based on a Boolean flag.

Categories