Data not reloading until refresh reactJS - node.js

I am currently building a website with two views in which a user can toggle between both views (images seen below) On both views I am allowing a user to update a database and both views display data regarding the state of that database. However, when I update the data on one toggle view of the data and switch to the other view, I can't see the updated data until I refresh.
I am wrapping my react application around this user context provider shown below
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [user, setUser] = useState();
const [courseList, setCourseList] = useState();
const [semesterList, setSemesterList] = useState();
const [userMajor, setMajor] = useState();
const checkContext = useCallback(() => {
axios
.get("/auth/check", { withCredentials: true })
.then(({ data }) => {
if (data.auth) {
setIsAuthenticated(true);
setUser(data.user);
setCourseList(data.courseList);
setSemesterList(data.semesterList);
setMajor(data.major);
setIsLoading(false);
} else {
setIsAuthenticated(false);
setMajor(undefined);
setCourseList(undefined);
setSemesterList(undefined);
setUser(undefined);
setIsLoading(false);
}
})
.catch(() =>
console.log(
"Something went wrong while trying to fetch your auth status."
)
);
}, []);
useEffect(() => {
checkContext();
}, [checkContext]);
return (
<UserContext.Provider
value={{
isLoading,
isAuthenticated,
user,
courseList,
semesterList,
userMajor,
checkContext,
}}
>
{children}
</UserContext.Provider>
);
};
export default UserContextProvider;
This is where I toggle between views
const Home = () => {
const [selected, setSelected] = useState(false);
const handleChange = () => {
setSelected(!selected);
setSelected(!selected);
};
let view;
if (!selected) {
view = <CardView />;
} else {
view = <SemesterView />;
}
return (
<>
<ToggleButton
className="toggler"
value="toggle"
selected={selected}
onChange={handleChange}
>
<ViewModuleIcon />
</ToggleButton>
{view}
</>
);
};
export default Home;
For keeping this shorter, here is one of views in which I update the database using a checkbox form.
const Home = () => {
const { courseList } = useContext(UserContext);
const initialMajorState = {
majorName: "",
majorCode: "",
majorComponentFamilies: [],
};
const [majorData, setMajorData] = useState(initialMajorState);
const [checkedCourses, setCheckedCourses] = useState(courseList);
const [componentLimit, setComponentLimit] = useState([]);
const [componentFamilyLimit, setComponentFamilyLimit] = useState([]);
const [isLoadingData, setIsLoadingData] = useState(true);
/**
* updates the checked courses, the database, as well as the limits for both components and componentfamilys
* #param checkedCourse is the course that is being checkedd
* #param checkedComponentName is the course family name that is being updated
* #param componentIndex is the course component to which the course belongs to
* #param componentFamilyIndex is which componentFamily the course belongs to
*/
const handleOnChange = (
checkedCourse,
checkedComponentName,
componentIndex,
componentFamilyIndex
) => {
// copy the componentLimit array
const updatedComponentLimit = [...componentLimit];
// copy the componentFamilyLimit array
const updatedComponentFamilyLimit = [...componentFamilyLimit];
const updatedCheckedCourses = checkedCourses?.includes(checkedCourse)
? checkedCourses?.filter((name) => name !== checkedCourse)
: [...(checkedCourses ?? []), checkedCourse];
let difference = updatedCheckedCourses.filter(
(x) => !checkedCourses.includes(x)
);
// This means that we added the course
if (difference.length > 0) {
// Add the course to the databas
UserCourseService.add(checkedCourse);
// Add the course to the checked list for that component
updatedComponentLimit[componentIndex].checked.push(checkedCourse);
// if that component is now satisfied then we want to add that to the component family's satisfied components
if (
updatedComponentLimit[componentIndex].checked.length >=
updatedComponentLimit[componentIndex].limit
) {
updatedComponentFamilyLimit[
componentFamilyIndex
].checkedComponentFamily.push(checkedComponentName);
}
}
// if the difference length is none then that means we unchecked a box
else {
// Delete the course from our database
UserCourseService.delete(checkedCourse);
// Add the course to the updatedComponentLimit array at the component index where course lies
updatedComponentLimit[componentIndex].checked.pop(checkedCourse);
// Pop that component family (pop will return null if not found anyways)
updatedComponentFamilyLimit[
componentFamilyIndex
].checkedComponentFamily.pop(checkedComponentName);
}
// set the componentFamilyLimit array and componentLimit array and checked courses
setComponentLimit(updatedComponentLimit);
setComponentFamilyLimit(updatedComponentFamilyLimit);
setCheckedCourses(updatedCheckedCourses);
};
/**
* returns a boolean determining if a checkbox is disabled or not
* #param course deals with which course we are checking should be disabled or not
* #param componentIndex deals with which component we are looking at
* #param componentFamilyIndex deals with which compoentFamily we are looking at
*/
const isDisabled = (course, componentIndex, componentFamilyIndex) => {
// if the component family limit has been reached then just return if the checkbox has already been checked
if (
componentFamilyLimit[componentFamilyIndex].checkedComponentFamily
.length >= componentFamilyLimit[componentFamilyIndex].limit
)
return componentLimit[componentIndex].checked.indexOf(course) === -1;
// if the component family limit has not been reached then check if the component itself has been meet and
// if the element has been checked already
else {
return (
componentLimit[componentIndex].checked.length >=
componentLimit[componentIndex].limit &&
// If not already checked
componentLimit[componentIndex].checked.indexOf(course) === -1
);
}
};
/**
* gets the data after searching for a course in the searchbar from the backend. Initializes the limit arrays and checked courses
* #param query is the name of the major we are getting data from in the backend
*/
const getData = async (query) => {
// Initializing the state for our global hooks in this function
var newCheckedComponentList = [];
var newCheckedComponentFamilyList = [];
// gets the response data in regards to the major we are interested in from the backend
MajorDataService.find(query)
.then((response) => {
// sets the major data to the response data
setMajorData(response.data);
// if the data exists then we want to initialize the other stuff
if (response.data.majorComponentFamilies) {
// Looping through the component families
response.data.majorComponentFamilies.forEach((componentFamily) => {
// gets the limit from the component family
var componentFamilyLimit = parseInt(
componentFamily.required_num_components
);
var newComponentFamilyList = [];
// Loops through each of the components in each component family
componentFamily.component_list.forEach((component) => {
// courselimit on each component
var courseLimit = parseInt(component.required_num_courses);
// the course list for a component
var newUserCourseList = [];
// Loops through all of the courses in each component
component.course_list.forEach((course) => {
// if the courselist includes the course then add that to the course list
if (courseList.includes(course)) {
newUserCourseList.push(course);
}
});
// Add to the checked array for components
newCheckedComponentList.push({
checked: newUserCourseList,
limit: courseLimit,
});
if (newUserCourseList.length === courseLimit) {
newComponentFamilyList.push(component.component_name);
}
});
newCheckedComponentFamilyList.push({
checkedComponentFamily: newComponentFamilyList,
limit: componentFamilyLimit,
});
});
setComponentLimit(newCheckedComponentList);
setComponentFamilyLimit(newCheckedComponentFamilyList);
setIsLoadingData(false);
}
})
.catch((e) => {
console.log(e);
});
};
const renderCourseRequirements = (
requiredCourses,
componentIndex,
componentFamilyIndex,
componentFamilyName
) => {
return (
<Grid container spacing={1} key={[uuid()]}>
{requiredCourses.map((course, index) => {
return (
<Grid key={[uuid()]} item xs={6} lg={4} xl={4}>
<Card
key={[uuid()]}
className={
checkedCourses.includes(course)
? "CourseCardCompleted"
: "CourseCard"
}
variant="outlined"
>
<CardHeader
key={[uuid()]}
action={
<Checkbox
name={course}
value={course}
className="header"
disabled={isDisabled(
course,
componentIndex,
componentFamilyIndex
)}
checked={checkedCourses.includes(course)}
onChange={() =>
handleOnChange(
course,
componentFamilyName,
componentIndex,
componentFamilyIndex
)
}
/>
}
title={
<Typography
key={[uuid()]}
className="CourseTitle"
color="textSecondary"
gutterBottom
>
{course}
</Typography>
}
></CardHeader>
<CardContent key={[uuid()]} className="content"></CardContent>
</Card>
</Grid>
);
})}
</Grid>
);
};
var componentCount = 0;
const renderComponents = (components, componentFamilyIndex) => {
return (
<Grid
container
spacing={1}
direction="column"
alignItems="center"
justifyContent="center"
key={[uuid()]}
>
{components.map((component, index) => {
var temp_index = componentCount;
componentCount = componentCount + 1;
return (
<Grid key={[uuid()]} item className="grid" xs={12} lg={4} xl={2}>
<Card
key={[uuid()]}
className={
componentLimit[temp_index].checked.length >=
componentLimit[temp_index].limit
? "ComponentCardCompleted"
: "ComponentCard"
}
variant="outlined"
>
<CardContent key={[uuid()]}>
<Typography
key={[uuid()]}
className="ComponentTitle"
color="textSecondary"
gutterBottom
>
{component.component_name}
</Typography>
<Typography
key={[uuid()]}
className="pos"
color="textSecondary"
>
Required # of Courses: {component.required_num_courses}
</Typography>
{renderCourseRequirements(
component.course_list,
temp_index,
componentFamilyIndex,
component.component_name
)}
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
);
};
const renderComponentFamilies = (majorComponentFamilies) => {
return (
<Masonry className="masonry" columns={3} spacing={2} key={[uuid()]}>
{majorComponentFamilies.map((componentFamily, index) => {
if (componentFamily.component_list.length > 1)
return (
<Card
key={[uuid()]}
className={
componentFamilyLimit[index].checkedComponentFamily.length >=
componentFamilyLimit[index].limit
? "ComponentFamilyCardCompleted"
: "ComponentFamilyCard"
}
variant="outlined"
>
<CardContent key={[uuid()]}>
<Typography
key={[uuid()]}
className="ComponentFamilyTitle"
color="textSecondary"
gutterBottom
>
{componentFamily.component_family_name}
</Typography>
<Typography
key={[uuid()]}
className="pos"
color="textSecondary"
>
Required # of Components :{" "}
{componentFamily.required_num_components}
</Typography>
{renderComponents(componentFamily.component_list, index)}
</CardContent>
</Card>
);
return (
<div key={[uuid()]}>
{renderComponents(componentFamily.component_list, index)}
</div>
);
})}
</Masonry>
);
};
return (
<div className="container home">
<SearchBar
key={[uuid()]}
placeholder="Enter Major Name ..."
data={MajorNames}
onChange={(value) => getData(value)}
/>
<div className="mui-grid">
{Array.isArray(majorData.majorComponentFamilies) &&
majorData.majorComponentFamilies.length > 0 &&
!isLoadingData ? (
<>
<h1>{majorData.majorName} </h1>
{renderComponentFamilies(majorData.majorComponentFamilies)}
</>
) : (
<></>
)}
</div>
</div>
);
};

Related

TypeError: skills?.filter is not a function

Making A portfolio website using react and nextjs, currently i am using hygraph to pull a list of skills.
attempted to pull the skills list, succeeded but then when it gets filtered the error above occurs
Skills.tsx
`
import type { NextPage } from "next";
import { ISkills } from "../typings";
import { Skill } from "./Skill";
interface ISKillsProps {
skills: ISkills[];
}
export const Skills: NextPage<ISKillsProps> = ({ skills }) => {
const languages = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "languages");
const frontend = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "frontend");
const uilibraries = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "uilibraries");
const headlessCms = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "headless cms");
const testing_tools = skills?.filter(
skill =>
skill?.fieldType?.toLowerCase() === "testing" || skill?.fieldType?.toLowerCase() === "tools"
);
const familiar = skills?.filter(skill => skill?.proficient === false);
return (
<>
<h1 className="skills_heading">Skills</h1>
<div className="skills_box">
<Skill skills={languages} skill="Languages" />
<Skill skills={frontend} skill="Frontend" />
<Skill skills={uilibraries} skill="UI Libraries" />
<Skill skills={headlessCms} skill="Headless CMS" />
<Skill skills={testing_tools} skill="Testing & Tools" />
<Skill skills={familiar} skill="Familiar" />
</div>
</>
);
};
services.ts
import { GraphQLClient, gql } from "graphql-request";
export const graphcms = new GraphQLClient(
"https://api-ca-central-1.hygraph.com/v2/cl9p263ni15rr01us16va0ugq/master"
);
export const QUERY = gql`
{
skills(orderBy: uniqueId_ASC) {
uniqueId
skill
id
proficient
fieldType
image {
url
}
url
}
}
`;
skills.tsx takes the query result and filters it before adding it as a html element.
services.ts makes a query to hygraph for my skills list.

React Typescript project .map function working only one time after compilation

I want to create a frontend for my app using React and TypeScript.
I am gathering data from my backend (simple DRF app) and fetching it using Axios and pushing it into separate arrays.
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
let idArr:number [] = new Array()
let titleArr:string [] = new Array()
let contentArr:string [] = new Array()
myApi.get('/getallarticles').then( res => {
for(let elem in res.data)
{
idArr.push(res.data[elem].id)
titleArr.push(res.data[elem].title)
contentArr.push(res.data[elem].content)
}
})
The app contains a page named feed into which I parse the id Array.
function App() {
console.log(idArr)
return (
<Box>
<Navbar/>
<Grid alignItems={"center"} justifyContent={"center"} marginLeft={'15%'} marginRight={'15%'}>
<Stack spacing={2} margin={2}>
<Box id='res_viewer'>
<Feed content={"CONTENT FROM MAIN"} id={10} title={"Sherlock Holmes"} total={5} idArr={idArr}/>
</Box>
</Stack>
</Grid>
</Box>
);
}
Then in my page Feed I copy the ActionCard (from Mui5) and the problem is that all the Cards show only if I recompile the project. It only works one time, and I have no idea why. Moreover if I put the console.log and alert into the Feed the console.log would show an array every time, while Alert only the first time. Anyone have any idea what the issue might be?
const Feed = ({ id, title, content, total, idArr }: { id: number; title: string; content: string; total: number; idArr:Array<number>}) => {
console.log(idArr)
alert(idArr)
return(
<div>
{idArr.map(customId =>
<Card key={customId} variant="outlined" sx={{ margin: 0 }}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{customId}
</Typography>
<Typography variant="body2" color="text.secondary">
{content}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button style={{
color: "#ff0000",
}} size="large" startIcon={<DeleteForever />}>
DELETE
</Button>
<Button style={{
color: "#68ee06",
}} size="large" startIcon={<Edit />}>
EDIT
</Button>
</CardActions>
</Card>
)}
</div>
)
}
export default Feed
I suspect the asynchronous nature of the call to your API is what's giving this behavior. When making calls to an API for data, it's common to use a useEffect hook that updates a state. This ensures that the UI is in sync with the data. Try rewriting as follows:
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
const [idArr, setIdArr] = useState([] as number[]);
const [titleArr, setTitleArr] = useState([] as string[]);
const [contentArr, setContentArr] = useState([] as string[]);
useEffect(() => {
myApi.get('/getallarticles').then(res => {
const newIds: number[] = [];
const newTitles: string[] = [];
const newContents: string[] = [];
for (let elem in res.data) {
newIds.push(res.data[elem].id);
newTitles.push(res.data[elem].title);
newContents.push(res.data[elem].content);
}
setIdArr(newIds);
setTitleArr(newTitles);
setContentArr(newContents);
});
}, []);

Pagination in NextJs

I am trying to paginate one of my pages in the application which is built with React / NextJs - getServerSideProps.
Step 1: Creates a pagination component
Step 2: Redirect to a URL with Page numbers (based on user clicks)
Step 3: It should re-render getServerSideProps with the newer page value, which is not happening right now.
My current code block (Server Side Props - API call):
export const getServerSideProps = async (ctx) => {
try {
const APIKey = await getCookieAPIKey(ctx);
const user = await getCookieUser(ctx);
const dataSSR = await getDataSSR(
APIKey,
'/xyz/xyz/read/',
user.user_id,
'user_id',
ctx.query.page,
ctx.query.limit
);
// console.log(d, "data")
return {
props: {
dataSSR
}
};
} catch (err) {
...
return { props: { fetchError: err.toString() } };
}
};
export const getDataSSR = async (APIKey, path, id, idString, page, limit) => {
//generate URL path for fetch
const base_url = `${ENDPOINT}/services`;
let url;
if (id && !idString && !page) {
url = base_url + path + '?key=' + APIKey + '&id=' + id;
} else if (id && idString && page) {
url = base_url + path + '?key=' + APIKey + `&${idString}=` + id + '&page=' + page + `&limit=${!limit ? '24' : limit}`;
} else if (id && idString && !page) {
url = base_url + path + '?key=' + APIKey + `&${idString}=` + id + '&page=0' + `&limit=${!limit ? '24' : limit}`;
}
else {
url = base_url + path + '?key=' + APIKey + '&page=' + page + `&limit=${!limit ? '10' : limit}`;
}
I followed this tutorial for pagination.
With a modification of the click method statement:
<ReactNextPaging
itemsperpage={itemsperpage}
nocolumns={nocolumns}
items={items}
pagesspan={pagesspan}
>
{({
getBackButtonProps,
getFwdButtonProps,
getFastFwdButtonProps,
getSelPageButtonProps,
nopages,
inipagearray,
pagesforarray,
currentpage,
noitems,
initialitem,
lastitem,
goBackBdisabled,
goFastBackBdisabled,
goFwdBdisabled,
goFastFwdBdisabled
}) => (
<tbody style={{ alignItems: "center", margin: "auto auto" }}>
{/* {items.slice(initialitem, lastitem).map((item, index) => {
return item;
})} */}
{noitems > 0
? [
<tr key={"pagingrow" + 100} >
<td colSpan={nocolumns} style={{ textAlign: "center" }}>
<button
style={buttonStyles(goBackBdisabled)}
{...getBackButtonProps()}
disabled={goBackBdisabled}
>
{"<"}
</button>
{Array.from(
{ length: pagesforarray },
(v, i) => i + inipagearray
).map(page => {
return (
<button
key={page}
{...getSelPageButtonProps({ page: page })}
disabled={currentpage == page}
style={{ margin: "0.5em", backgroundColor: "transparent", border: "none" }}
onClick={e => page != currentpage ? pageNumClick(page, e, currentpage) : {}}
>
{page}
</button>
);
})}
<button
style={buttonStyles(goFwdBdisabled)}
{...getFwdButtonProps()}
disabled={goFwdBdisabled}
>
{">"}
</button>
</td>
</tr>
]
: null}
</tbody>
)}
</ReactNextPaging>
Page redirection handle code :
const pageNumClick = (page, e, currentpage) => {
let el = document.getElementsByClassName(`.clickable-page-${page}`)
console.log(el)
e.target.style.backgroundColor = "#353E5A";
currentpage = page;
console.log(page, "clicked page number", e.target, currentpage)
//Redirects to the URL with clicked page number
router.push({
pathname: router.pathname,
query: { show: showname, page: page }
})
refreshData(); // Try to refresh props once the URL is changed
}
const refreshData = () => {
router.replace(router.asPath);
console.log('refreshed')
}
Attempts to resolve:
Added refreshData method to invoke ServerSideProps upon URL change based on this.
Tried changing getServerSideProps to getInitialProps - with no luck
Any help or links would be appreciated, been stuck with the task since 3 days
Issue is caused by the refreshdata function, router.asPath will have your current url.
Below code is working fine for me.
function ProductDetail({ products, page,limit }) {
const router = useRouter();
const pageNumClick = (page, limit) => {
router.push({
pathname: router.pathname,
query: { limit: limit, page: page },
});
};
return (
<div>
<div onClick={() => pageNumClick(parseInt(page) + 1, limit)}>Next page</div>
<div onClick={() => pageNumClick(parseInt(page) - 1, limit)}>
Previous page
</div>
{products ? JSON.stringify(products) : <></>}
</div>
);
}
export async function getServerSideProps({ params, query, ...props }) {
const products = await getProducts(query.limit, query.page);
return {
props: {
products: products ? products : {},
page: query.page,
limit: query.limit,
},
};
}

How can I modify this code so that render waits for Promise Items?

I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.

Cannot match dynamic routes like /recipes/:id/ingredients

I am using the npm package react-router-dom#4.0.0-beta.6 for my react app.
I am able to match routes like:
/trends
/news
/recipes
But I can't match routes like:
/recipes/:id/ingredients or /recipes/1234/ingredients
If I switch from /recipes/1234 to /recipes/1234/ingredients I only match the route /recipes/:id but not /recipes/:id/ingredients.
The app looks like this:
const factory = (name, path) => {
return (props) => {
const { children, match, routes } = props;
const { params } = match;
// convert the child routes from /recipes/:id/ingredients to /recipes/1234/ingredients
const others = routes.map(x => pathToRegexp.compile(x.path)(params));
return (
<div>
<h3>{name}</h3>
<ul>
{others.map(x =>
<Link key={x} to={x}>{x}</Link>
)}
</ul>
{children}
</div>
);
};
};
const rootPattern = '/recipes/:id';
const routes = [
{
path: rootPattern,
component: factory('Root', rootPattern),
routes: [
{
path: `${rootPattern}/ingredients`,
component: factory('Ingredients', `${rootPattern}/ingredients`),
},
{
path: `${rootPattern}/steps`,
component: factory('Steps', `${rootPattern}/steps`),
},
],
},
];
// wrap <Route> and use this everywhere instead, then when
// sub routes are added to any route it'll work
const RouteWithSubRoutes = (route) => (
<Route path={route.path} render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes}/>
)}/>
);
const RouteConfigExample = () => (
<Router>
<div>
<ul>
<li><Link to="/recipes/1234">Sandwiches</Link></li>
</ul>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route}/>
))}
</div>
</Router>
);
To be fair I don't really understand why my app doesn't match the dynamic routes.
You can find a complete example at https://github.com/jbrieske/react-router-config.

Resources