NEXT.JS 13 Send props to Layout - javascript

I have a NEXT.js app, After i select school .
Im tryna pass data to my vertical nav bar. It works fine, in dev mode.
But it the data is always undefined in production mode. The getdata function doesnt work. I think im doing something silly, instead of passing data down and down to components, is there a better way ?
Code :
App/school/[school_id]/page.tsx
async function Page(searchParams : unknown) {
Here im getting the the data from the <Link/> component
await getNavIDS(searchParams);
//here im tryna send my datqa to my navbar
return (
<>
<div className="row">
<div className="col-1">
21312312321231
</div>
</div>
</>
);
}
// NAVBAR COMPONENT
var navObject : any = {id:""};
export const getNavIDS = async(params : {}) => {
navObject= await params;
}
function Verticalnav() {
console.log()
return (
<>
<nav>
<Link href={{pathname:`school/${navObject.id}/students`}}><button type="button" className="btn btn-dark btn_size">All Students</button></Link>
</nav>
</>
)
}
export default Verticalnav
// I Have a page that displays cards. With a button which directs with the component , shown below
<div className='School_box'>
<Link href={{pathname:`school/${item.id}`, query: item}} ><button type="button" className="btn btn-secondary btn-lg">View School</button></Link>
</div>
</div>
I've tried adding a function to GRAB the data. but sometimes the navbar gets rerendered and i lose the props. I cannot find this on next.js docs .
Should i use a router instead ? NO IDEA. Some feedback would be appreciated

Related

How to refetch properly an API request after an event occurred?

i'm a react beginner and I'm struggling with the refresh of the components.
In this case, I have a GET request to an API triggering in a useEffect hook with no dependencies on my App.js. Of course, this is working just fine when my app just starts. However, I need it to trigger again after certain events ocurre, like an Onclick event in a button, and a OnSubmit event in a form, in order to re render the updated table component where the API data is displayed.
What is the best way to do this?
I thought about just calling the fetch function again whenever I need to update the data, but I don't know if that's the way to go.
Then, I currently have this workaround function to force the reload of the entire page after the events trigger, but i'm pretty sure this is not proper either:
const refresh = () =>{
window.location.reload(false);
}
I've been also tweaked the dependencies in the use effect (where the fetch happens) a little bit, because I'm feeling like the solution is there, but I had only bad results, like infinite loops.
Finally guys, I'll post an overview pic of my App code. Have in mind that I need to do the refresh in the next components: OrdenPago y Grid.
import { useState, useEffect } from 'react';
import { OrdenPago } from './OrdenDePago';
import EstadoCvus from './EstadoCvus';
import Grid from './Grid';
import axios from 'axios';
export const App = () => {
const [ops, setOps] = useState({ ordenesPago: [] });
useEffect( () => {
axios.get("http://cpawautojava:1400/consultaOrdenesPago")
.then(response => {
setOps(response.data);
console.log(response.data);
}).catch((error) => { console.log(error) })
}, [])
return (
<div className="container">
<div className='row'>
<div className='col-12'>
<div className='text-white py-2' style={{ backgroundColor: "#414BB2", height: "40px" }} >
<h1 className='text-center lead fw-bold'>GestiĆ³n de OPs</h1>
</div>
</div>
</div>
<div className='row mt-3' >
<div className='col-6 d-flex justify-content-center'>
<OrdenPago />
</div>
<div className='col-6 form1' >
<EstadoCvus />
</div>
</div>
<div className='row'>
<div className='col-12' >
<Grid ops={ops} />
</div>
</div>
</div>
);
}
Thanks in advance for your help.
Rodrigo.

Conditional render of props in react

I have this 2 states, from 2 different api calls
one is 'movieList' and the other one is 'search', Both are array of movies.
movieList is automatically rendered since it is the search for popular movies and it corresponds that the user is shown as soon as he opens the page, in the navbar I have an input attached to a state called search and it saves an array of movies that match the name in it ... try to use a conditional with the following logic, if search exists, maps search, otherwise map movieList.
but it seems I don't know how to do it correctly. If someone can give me a hand in how to do it, it would help me a lot, thank you very much! here I leave the code
import { useSelector } from 'react-redux'
import { getAllMovies } from '../../features/movieSlice'
import MovieCard from '../MovieCard/MovieCard';
const MovieListing = ({ movieList, search }) => {
return (
<div className='' >
<div className=''>
<div className='my-20 mx-15 flex flex-wrap justify-around items-center' >
{
movieList.map((movie)=>(
<MovieCard {...movie} key={movie.id} />
))}
</div>
</div>
</div>
)
}
export default MovieListing```
To do a conditional render you can try use a ternary operator:
<div className='' >
<div className=''>
<div className='my-20 mx-15 flex flex-wrap justify-around items-center' >
{
search ? search.map((searchItem) => <MapsSearch {...search}/> :
movieList.map((movie) => (<MovieCard {...movie} key={movie.id}/>))
}
</div>
</div>
</div>
(You'll just need to modify the mapping of search and what it returns as I'm not sure what type search is)

How to take items from an array that is in a React component's props and render to the page?

I am trying to make my navbar dynamically show different things depending on whether the user is signed in or not using React and Redux. And actually, that has proven to be manageable, I hooked up action creators that check whether there is a user object and if there is then to switch the isSignedIn property to true and show content accordingly.
The challenge has been that I want to show that user's profile photo in the navbar too. In order to do that, my plan was to have an action creator fire off a function that will get the user's documents form the database (firebase) and return the details in state to the navbar component. Then mapStateToProps would take that content and move it into props for me to manipulate onto the users view.
I can get this all to work to a point, the component fires up and it takes a bit of time to get the users details form firebase, so theres a couple of seconds where the props.user property is empty. That's fine but then when it does return with the users content, they are stored in object in an array and I have no idea how to access them.
So this.props.user is an array with one object that has key-value pairs like userDateOfBirth: "12-12-2020" and so on. I need to access those values. I thought either this.props.user.userDateOfBirth or this.props.user[0].userDateOfBirth would work but neither does. The first just returns 'undefined' and the second returns an error "Cannot read property 'userDateOfBirth' of undefined.
Please help, this is driving me insane.
I've updated the question to include some of the code that should make this a bit more understandable. I've left the render function out of the navbar component for the sake of brevity.
The Action Creators signedIn and signedOut work as I expected. It's the currentUser Action Creator which I'm having difficulties with. I've shared as much as I think necessary, and maybe a little too much. Thanks for any help.
Code Below:
* Navbar Component *
import './NavBar.css';
import {Link} from "react-router-dom";
import React from "react";
import {connect} from "react-redux";
import * as firebase from "firebase";
import {signedIn, signedOut, currentUser} from "../actions";
componentDidMount()
{
this.unregisterAuthObserver = firebase.auth().onAuthStateChanged((user) => {
let currentUser = user;
if (user) {
this.props.signedIn()
} else this.props.signedOut();
if (this.props.auth.isSignedIn) {
this.props.currentUser(currentUser.uid);
}
});
}
render() {
return (
this.props.auth.isSignedIn ?
//If the user is signed in, show this version of the navbar
//Overall NavBar
<div id="navBar">
{/*Left side of the NavBar*/}
<div id="navBarLeft">
<Link to ={"/"}
>
{/* OLAS Logo*/}
<img
alt={"Olas Logo"}
id="logo"
src={require("../img/Olas_Logo&Brandmark_White.png")}
/>
</Link>
</div>
{/*Right side of the NavBar*/}
<div id="navBarRight">
<div id="home" className="navBarItem">
<Link to={"/"} >
HOME
</Link>
</div>
<div id="careerPaths" className="navBarItem">
<Link to={"/careerPath"} >
CAREER PATHS
</Link>
</div>
<div id="jobPostInsights" className="navBarItem">
<Link to={"/jobPostInsights"} >
JOB POST INSIGHTS
</Link>
</div>
<div id="careerQ&AForum" className="navBarItem">
<Link to={"/forum"} >
CAREER Q&A FORUM
</Link>
</div>
<div id="userProfile" className="navBarItem">
<Link to={"/userprofile"}>
{this.props.user.length ? (<div>{this.props.user.user[0].userFirstName}</div>): <div>PROFILE</div>}
</Link>
</div>
</div>
</div>
//This is a critical part of the ternary operator - DO NOT DELETE
:
//If the user is not signed in, show this version of the navbar
<div id="navBar">
{/*Left side of the NavBar*/}
<div id="navBarLeft">
<Link to ={"/"}
>
{/* OLAS Logo*/}
<img
alt={"Olas Logo"}
id="logo"
src={require("../img/Olas_Logo&Brandmark_White.png")}
/>
</Link>
</div>
{/*Right side of the NavBar*/}
<div id="navBarRight">
<div/>
<div/>
<div id="about" className="navBarItem">
<Link to={"about"}>
<span className="navBarItemInner">ABOUT OLAS</span>
</Link>
</div>
<div id="home" className="navBarItem">
<Link to={"/"} >
<span className="navBarItemInner">HOME</span>
</Link>
</div>
<div id="signIn" className="navBarItem" >
<Link to={"/signIn"} >
<span className="navBarItemInner">SIGN IN / REGISTER</span>
</Link>
</div>
</div>
</div>
)};
const mapStateToProps = state => {
console.log(state);
return {auth: state.auth, user: state.user}
};
export default connect(mapStateToProps, {signedIn, signedOut, currentUser})(NavBar);
* currentUser Action Creator *
export const currentUser = (currentUserUid) => {
return async (dispatch) => {
const response = await getUserDocFromDB(currentUserUid);
dispatch({
type: GET_CURRENT_USER,
payload: response,
});
}
};
* getUserDocFromDB *
import getFirestoreDb from '../getFirestoreDb';
let db = getFirestoreDb();
const getUserDocFromDB = async (currentUserUid) => {
let userDoc = [];
await
db.collection("users")
.where("userFirebaseUid", "==", currentUserUid)
.onSnapshot(
(querySnapshot) => {
if (querySnapshot.empty) {
return;
}
querySnapshot.forEach((doc) => {
const {
userDateOfBirth,
userGender,
userState,
userCity,
userTitle,
userDescription,
userFirstName,
userLastName,
userProfileImageUrl,
} = doc.data();
userDoc.push({
userDateOfBirth,
userGender,
userState,
userCity,
userTitle,
userDescription,
userFirstName,
userLastName,
});
});
},
function(error) {
console.log("Error getting document Error: ", error);
},
);
return userDoc;
};
export default getUserDocFromDB;
* User Reducer *
import {GET_CURRENT_USER} from "../actions/ActionTypes";
const INITIAL_STATE = {
user: null,
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_CURRENT_USER:
return {user: action.payload};
default:
return INITIAL_STATE;
}
};
* Combine Reducers *
import { combineReducers } from "redux";
export default combineReducers({
user: UserReducer,
});
The second way, this.props.user[0].userDateOfBirth is the correct way of accessing user properties after the data is loaded. What is happening is the array is empty while the data is being fetched and populated in app state. Using a guard will prevent the undefined error.
Assuming the default state is an empty array:
this.props.user.length && this.props.user[0].userDateOfBirth
In react this is a pattern called Conditional Rendering, and would likely look more like:
{this.props.user.length ? (
/* Render user component using this.props.user[0] */
) : null}
To make consuming the user object a little cleaner in components I'd convert the default state to be null, and have the reducer that handles the fetch response do the unpacking from the response array. Then the check is reduced to something like:
{this.props.user ? (
/* Render user component using this.props.user */
) : null}
I hope you are using private route then simply get pathname by using window.location.pathname === "user/dashboard" ? print username : "login "
Minimize tge filtration ..
Happy coding
Drew Reese's answer is spot on and I just wanted to add to it. You can check if the property exists, or simply render nothing if does not, as his examples show. When the state finally loads, it will force a re-render, the conditional will then be true, and you can then show the user profile photo or whatever other components require info from the returned api call.
This is why you might have seen a pattern of dispatching LOADING reducers, setting loading to true until the api returns and then it is set to false. You can check if loading is true in the component, and choose to render a spinner or something else instead.

How do I go to previous page in gatsby (history.goBack)

I am working on gatsby. I need to go back to privious page/link as I used to do with reactjs.
<a onClick={() => this.props.history.goBack}>
<button type="button" className="close_tab">
<img src={Close} alt="" />
</button>
</a>
How can I do this using gatsby?
Use navigate(-1):
import React from "react";
import { navigate } from "gatsby";
export default function GoBack() {
return (
<Button onClick={() => navigate(-1)}>
Go Back
</Button>
);
}
Edit: Since reach-router#1.3.0, you can now simply call navigate(-1) to go back. Manually update reach-router in your Gatsby project if it's not yet updated. Thanks #nathan in the comment for the tip.
Edit: Ah alright, I've just realized this.props.history.goBack is a react-router thing. Gatsby doesn't use react-router, but reach-router under the hood and it doesn't have the history props or the goBack method. There's a issue requesting to add this, but wasn't implemented. You'd have to use browser's own history object as I suggested below.
import React from 'react'
const BackButton = React.forwardRef(
({ children, ...props }, ref) => {
const onClick = e => {
e.preventDefault()
history.back()
}
return (
<a {...props} ref={ref} href="#" onClick={onClick}>
{children}
</a>
)
}
)
BackButton.displayName = 'BackButton'
export { BackButton }
Is this.props.history the browser's history? If so, you can do this.props.history.go(-1) to go back to the previous page.
As always with Gatsby, watch out when you use methods from browser, since they don't exist during html generation:
export default () => (
<button onClick={() => {
typeof history !== 'undefined' && history.go(-1)
}}>back</button>
)
For a function component in Gatsby:
<a onClick={() => window.history.back()}>Go back</a>
The gatsby navigate function is type as NavigateFn.
Which you can find declare as:
export interface NavigateFn {
(to: string, options?: NavigateOptions<{}>): Promise<void>;
(to: number): Promise<void>;
}
So, as you can see, you either can pass the route you want to redirect to, or an specific number.
Try with navigate(-1)
This should work
import { navigate } from "#gatsbyjs/reach-router";
<button onClick={() => navigate(-1)}>Back to previous page</button>
It goes to the previous page

React - selected value is not displayed after refreshing page

So, I'm using react-select to let the user pick a choice from a list of options. It's supposed to update on-change. I've verified that the selected option is indeed being updated into the database, and the input is being recognized by React upon checking it in the React chrome tools. What's puzzling is how it doesn't get displayed after refreshing the page.
class ContractBasicForm extends React.Component {
constructor (props) {
super(props)
this.state = {
contractingEntity: props.contracting_entity
}
componentWillReceiveProps(nextProps) {
this.setState({
contractingEntity: nextProps.contracting_entity
})
}
autoSetState = (newState) => {
this.setState(newState)
this.props.formSubmit()
}
render () {
return(
<div className="container-fluid">
<section className="row ml-2 mr-2 mt-2">
<article className="col-12 side-modal-form">
<SelectInput
header="Contracting Entity"
name="contract[contracting_entity]"
options={this.props.contracting_entity_opts}
value={this.state.contracting_entity}
userCanEdit={this.props.user_can_edit}
multi={false}
onChange={(e) => {
this.autoSetState({contracting_entity: e.value})
}}
/>
</article>
</section>
</div>
)
}
}
I have another input called Stage which is very similar to ContractingEntity, but its value is displayed after refreshing the page:
<SelectInput
header="Stage"
name="contract[stage]"
options={this.props.stage_opts}
value={this.state.stage}
userCanEdit={this.props.user_can_edit}
multi={false}
onChange={(e) => {
this.autoSetState({stage: e.value})
}}
/>
React app state will be initialised on page refresh. You need to persist such data in localStorage if you want to keep it after page refresh. This is considered as anti-pattern in react and it is recommended not to use this unless it becomes necessity.
I hope this made things clear for you.

Categories

Resources