I'm trying to make my page SEO friendly by using getServerSideProps method. I have tried to google it but cannot find any specifc example for my case. I'm not sure how to do it. I have a useEffect method currently.
import Header from './Header.js';
import Footer from './Footer.js';
import React, { Component, useState, useEffect } from 'react';
import Link from 'next/link';
import { useParallax, ParallaxBanner } from "react-scroll-parallax";
import { useTranslation } from "react-i18next";
import axios from "axios";
//import { Link, useLocation } from 'react-router-dom';
import Post from "./Post";
function Article({postSlug}) {
const { t, i18n } = useTranslation();
const [post, setPost] = useState([]);
const [posts, setPosts] = useState([]);
const currentlocale = typeof window !== 'undefined' ? localStorage.getItem('locale') : null;
useEffect(() => {
if(postSlug){
axios({
method: "POST",
url: process.env.NEXT_PUBLIC_API_URL + '/pages/article',
headers: { 'Content-Type': 'application/json;charset=UTF-8', "Access-Control-Allow-Origin": "*", "Accept": "application/json" },
data: {
country: currentlocale,
slug: postSlug
}
}).then(result => {
setPost(result.data.json.article);
setPosts(result.data.json.articles);
});
}
}, [postSlug])
return (
<div id="web-front">
<Header />
{post.title}
<Footer />
</div>
)
}
export default Article;
getServerSideProps only runs on routes / files created inside /pages folder.
Move your data fetching methods from the component to the getServerSideProps function.
You can get postSlug from the context.req[1] object getServerSideProps receives.
LocalStorage won't be available on server. Instead use cookies as a replacement for localStorage, since cookies is available on the context.req object. [2]
function Article({ initialPost, initialPosts }) { //👈 received from ssr props
const { t, i18n } = useTranslation();
const [post, setPost] = useState(initialPost); // set initially
const [posts, setPosts] = useState(initialPosts); // set initially
return (
<div id="web-front">
<Header />
{post.title}
<Footer />
</div>
);
}
export async function getServerSideProps(ctx) {
const postSlug = ctx.req.params.postSlug; // 👈 handled by you
const currentLocale = getCurrentLocaleFromCookies(ctx.req); // 👈 handled by you
let initialData = {};
try {
initialData = await axios({
method: 'POST',
url: process.env.NEXT_PUBLIC_API_URL + '/pages/article',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin': '*',
Accept: 'application/json',
},
data: {
country: currentlocale,
slug: postSlug,
},
});
} catch (err) {
console.error(err);
}
return {
props: {
initialPost: initialData.data.json.article,
initialPosts: initialData.data.json.articles,
},
};
}
export default Article;
Reference:
https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props
https://stackoverflow.com/a/63875689/7679903
Related
I have little environment in nodejs with mongodb and express-session, i follow react router tutorial https://reactrouter.com/en/main/start/tutorial , and it's fine. But when i decide to make two children from layour route with two different actions like register and login on different submit the same action is throw. I can't find anything to solve it. Why on different action children my app read the same action for both?
APP.JSX FILE
import './App.css';
import React, { useState } from 'react';
import {
createBrowserRouter,
RouterProvider,
Route,
} from "react-router-dom";
import Register, { registerAction } from './component/Register';
import Login, { loginRequest } from './component/Login';
import Simplejs, {loader as isAuthLoader } from './component/SimpleJs';
import Layout from './component/Layout';
import ErrorPage from './component/ErrorLayout';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
errorElement: <ErrorPage />,
children: [
{
path:"",
element: <Login />,
action: loginRequest,
},
{
path: "",
element: <Register />,
action: registerAction,
},
]
},{
path:"/index",
element: <Simplejs />,
errorElement: <ErrorPage />,
loader: isAuthLoader,
children: [
]
}
], {basename: "/"});
function App() {
return (
<section>
<RouterProvider router={router} />
</section>
);
}
REGISTER ACTION:
export async function registerAction({ request }){
const formData = await request.formData();
const updates = Object.fromEntries(formData);
await Registration(updates);
}
REGISTRATION HANDLER:
export const Registration = async (object) => {
console.log('Registration');
const config = {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true,
credentials: 'include'
}
const body= object;
try {
const res = await axios.post('/registration', body, config)
console.log(res);
const data = res.data
if(data) return {data}
} catch (error) {
if(error.response.data.msg){
console.log(error.response.data.msg);
}else if(error){
console.log(error)
}else{
console.log('errore');
console.log(error);
}
}
}
LOGIN ACTION:
export async function loginRequest({ request }){
const formData = await request.formData();
const updates = Object.fromEntries(formData);
await LoginAccess(updates);
}
LOGIN HANDLER:
export const LoginAccess = async (object) => {
console.log('Login');
const config = {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true,
credentials: 'include'
}
const body= object;
try {
const res = await axios.post('/login', body, config)
console.log(res);
const data = res.data
if(data) return {data}
} catch (error) {
if(error.response.data.msg){
console.log(error.response.data.msg);
}else if(error){
console.log(error)
}else{
console.log('errore');
console.log(error);
}
}
}
I want to have a screen which contains all my favorite subjects. I have to select these from another screen, with all the subjects. The functionallity behind it works fine, but the problem I'm having right now, is that when a subject is chosen as a favorite, it doesn't show up on the favorite screen... Only after a forced reload... Any ideas how I can fix this?
import {
Button,
Text,
View,
StyleSheet,
FlatList,
SafeAreaView,
StatusBar,
ScrollView,
} from "react-native";
import React, {Component, useContext, useState} from "react";
import Ionicons from 'react-native-vector-icons/Ionicons';
import {TouchableOpacity} from "react-native";
import getAccessToken from "../functions/getAccessToken";
import refreshToken from "../functions/refreshToken";
import Subject from "../functions/SubjectLoaders/Subject";
import styleSubjectList from "../styles/styleSubjectList";
import backendURL from "../backendURL";
import getFromStore from "../functions/getFromStore";
import {BottomTabBarHeightContext, useBottomTabBarHeight} from '#react-navigation/bottom-tabs';
import FavoriteSubject from "../functions/SubjectLoaders/FavoriteSubject";
import {AuthContext} from "../Authentication/AuthProvider";
function SubjectListScreen({navigation}) {
const [subjects, setSubjects] = useState([]);
const [details, setDetails] = useState([]);
const [hasLoaded, setHasloaded] = useState(false);
const tabBarHeight = useBottomTabBarHeight();
React.useEffect(()=> {
const constructor = async () => {
await refreshToken();
let token = await getAccessToken();
let ownId = await getFromStore("ownId")
console.log("ownId: " + ownId)
let axios = require('axios');
let config = {
method: 'get',
url: backendURL + '/subjectManagement/subjects',
headers: {
'Authorization': 'Bearer ' + JSON.parse(token)
}
};
axios(config)
.then(function (res) {
// console.log(res.data)
setSubjects(res.data);
setHasloaded(true);
//console.log(res.data);
}).catch(function (error) {
});
}
constructor();
},[])
// if(!hasLoaded) return null;
return(
<View style={styleSubjectList.container}>
<SafeAreaView style={{justifyContent: 'center',}}>
<FlatList
data={subjects}
renderItem={ ({item}) => {
return <FavoriteSubject subject={item}/>
}}
keyExtractor={ subject => subject.id }
/>
</SafeAreaView>
</View>
)
}
export default SubjectListScreen;
This is the page that should reload!
import React, {useContext, useState} from "react";
import { Pressable } from "react-native";
import { MaterialCommunityIcons } from "#expo/vector-icons";
import styleSubjectList from "../../../styles/styleSubjectList";
import refreshToken from "../../refreshToken";
import getAccessToken from "../../getAccessToken";
import getFromStore from "../../getFromStore";
import addToFavorites from "./addToFavorites";
import removeFromFavorites from "./removeFromFavorites";
import backendURL from "../../../backendURL";
import axios from "axios";
import removeFirstAndLast from "../../removeFirstAndLast";
import {AuthContext} from "../../../Authentication/AuthProvider";
import isRole from "../isRole";
const Hart = ({subject}) => {
const { userInfo } = useContext(AuthContext);
const [liked, setLiked] = useState(false);
const [hasloaded,setHasloaded] = useState(false);
const [ownId, setOwnId] = useState('');
const [token, setToken] = useState('');
const [favourite, setFavourite] = useState([]);
const favouriteId = [];
React.useEffect(()=> {
const constructor = async () => {
await refreshToken();
let token = await getAccessToken();
setToken(token);
let id = await getFromStore("ownId");
id = removeFirstAndLast(id)
setOwnId(id)
const axios = require('axios');
const config = {
method: 'get',
url: backendURL + '/userManagement/users/' + id,
headers: {
'Authorization': 'Bearer ' + JSON.parse(token)
}
};
axios(config)
.then(function (response) {
// setFavourite(response.data.favouriteSubjects)
for(let i = 0; i<response.data.favouriteSubjects.length; i++) {
if(response.data.favouriteSubjects[i].id === subject.id) setLiked(true)
}
})
}
constructor().then(() => {
setHasloaded(true);
}).catch(e=>console.log(e));
},[])
const checkFavorite = () => {
setLiked((isLiked) => !isLiked)
if(liked) {
console.log("verwijderen uit favorieten");
removeFromFavorites(subject,ownId, token);
}
else {
console.log("toevoegen aan favorieten");
addToFavorites(subject,ownId,token);
}
}
// console.log(favourite)
if(!hasloaded || isRole("ROLE_COORDINATOR",userInfo) || isRole("ROLE_PROMOTOR",userInfo) || isRole("ROLE_CONTACT",userInfo) || isRole("ROLE_ADMIN",userInfo)) return null;
else{
return (
<Pressable style={styleSubjectList.heartIcon} onPress={ () => checkFavorite()}>
<MaterialCommunityIcons
name={liked ? "heart" : "heart-outline"}
size={20}s
color={liked ? "red" : "white"}
/>
</Pressable>
);
}
};
export default Hart;
The function addToFavorites/removeFromFavorites should trigger the change...
import removeFirstAndLast from "../../removeFirstAndLast";
import backendURL from "../../../backendURL";
const removeFromFavorites = async (subject,ownId,token) => {
let axios = require('axios');
let qs = require('qs');
let data = qs.stringify({
'userId': ownId,
'subjectId': subject.id,
});
console.log(data)
let config = {
method: 'delete',
url: backendURL + '/userManagement/users/student/favouriteSubject',
headers: {
'Authorization': 'Bearer ' + JSON.parse(token),
'Content-Type': 'application/x-www-form-urlencoded'
},
data : data
};
axios(config)
// .then(function (response) {
// console.log(JSON.stringify(response.data));
// })
.catch(function (error) {
console.log(error);
});
}
export default removeFromFavorites;
use key={Date.now()} on the component your wanted to be changed.
I use React for frontend and Node.js in backend and Postgre for database.
I have create my own API for authenticating user and using useState and useContext hook to store the login status of the user.
I also setup a Redirect function after successful login but the useState is taking a while to update the login status of user and because of that the page is not being redirect.
I tried using async and await while fetching the data from the server but still there is delay in authenticating the user.
I also tried to follow some blogs like this
This context state handle the login functionality and update the login status within the component.
import React, { createContext, useContext, useState } from "react";
import { GlobalContext } from "./GlobalState";
export const LoginAuth = createContext();
export const ProvideAuth = ({ children }) => {
const auth = useProvideAuth();
return <LoginAuth.Provider value={auth}>{children}</LoginAuth.Provider>;
};
export const useAuth = () => {
return useContext(LoginAuth);
};
const useProvideAuth = () => {
const [user, setUser] = useState(null);
const { setIsLogin } = useContext(GlobalContext);
const login = async (userDetails) => {
const response = await fetch("http://localhost:4000/api/v1/login/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(userDetails),
}).catch((err) => console.log(err.message));
const data = await response.json();
if (data?.error) {
setUser(false);
} else {
setUser(data);
setIsLogin(true);
}
};
return { user, login };
};
This state used to update the login state throughout the app
import React, { createContext, useState } from "react";
export const GlobalContext = createContext();
export const GlobalProvider = ({ children }) => {
const [isLogin, setIsLogin] = useState(false);
return (
<GlobalContext.Provider value={{ isLogin, setIsLogin }}>
{children}
</GlobalContext.Provider>
);
};
Private Route Code
import React, { useContext } from "react";
import { Redirect, Route } from "react-router";
import { GlobalContext } from "../State/GlobalState";
const PrivateRouteLogin = ({ children, ...rest }) => {
const { isLogin } = useContext(GlobalContext);
return (
<Route
{...rest}
render={({ location }) => {
return isLogin ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location },
}}
></Redirect>
);
}}
/>
);
};
export default PrivateRouteLogin;
You can have an another value for your loginStatus
false = not logged in
true = logged in
pending = for collect all data
in this situation you can have a backdrop(loading) that show over your website till detect loginStatus
I am using react and I created a auth file to store all of my api request related to authentication purposes. I have inside a default function signin with a signin request, and I also want to store a signup request in the same file. I believe I am doing something wrong as it is not rendering properly.
here is my code:
import React from 'react'
import axios from 'axios';
export const signin = async(data)=>{
const config = {
headers: {
'Content-Type': 'application/json'
},
};
const response = await axios.post('http://localhost:5000/api/auth/signin', data, config);
return response;
};
const signup = async(data)=>{
const config = {
headers: {
'Content-Type': 'application/json'
},
};
const rep = await axios.post('http://localhost:5000/api/auth/signup', data, config);
return rep;
};
export default signin;
Like #saeed stated in his comment, you could export both signin and signup normally and import them somewhere else using import {signin, signup} from 'access'.
import React from 'react';
import axios from 'axios';
export const signin = async (data) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const response = await axios.post(
'http://localhost:5000/api/auth/signin',
data,
config,
);
return response;
};
export const signup = async (data) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const rep = await axios.post(
'http://localhost:5000/api/auth/signup',
data,
config,
);
return rep;
};
And to import the functions:
import {signin, signup} from 'somefile'
i'm practicing reactjs watching this video https://www.youtube.com/watch?v=5rh853GTgKo&list=PLJRGQoqpRwdfoa9591BcUS6NmMpZcvFsM&index=9
I want to verify my information using uid and token, but I don't know how to deliver it.
In this code: Activate.js in container
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { verify } from '../actions/auth';
const Activate = ({ verify, match }) => {
const [verified, setVerified] = useState(false);
const verify_account = e => {
const uid = match.params.uid; // I Think This part is Problem
const token = match.params.token;
verify(uid, token);
setVerified(true);
};
if (verified) {
return <Redirect to='/' />
}
and this code : auth.js in actions
export const verify = (uid, token) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ uid, token });
try {
await axios.post(`${process.env.REACT_APP_API_URL}/auth/users/activation/`, body, config);
dispatch ({
type: ACTIVATION_SUCCESS,
});
} catch (err) {
dispatch ({
type: ACTIVATION_FAIL
});
}
}
i think i didn't render uid, token but i confused how to do that
App.js code:
<Router>
<Layout>
<Switch>
<Route exact path ='/activate/:uid/:token'>
<Activate />
</Route>
</Switch>
</Layout>
</Router>
I'd appreciate any help. :)
use the useParams hook to extract uid and token params:
import React, { useState } from 'react';
import { Redirect, useParams } from 'react-router-dom';
import { connect } from 'react-redux';
import { verify } from '../actions/auth';
const Activate = ({ verify }) => {
const [verified, setVerified] = useState(false);
const { uid, token } = useParams();
const verify_account = e => {
verify(uid, token);
setVerified(true);
};
if (verified) {
return <Redirect to='/' />
}