Is there any way how to create/structure next.js app for navigation without losing header component state?
Let me explain.
I have header component like this:
import { useState } from "react"
import Link from 'next/link'
export const Header = () => {
const [value, setValue] = useState(1)
return (
<header>
HEADER
<button onClick={() => setValue(value + 1)}>
{value}
</button>
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/test">
<a>About Us</a>
</Link>
</li>
</ul>
</header>
)
}
export default Header
There is a easy couter.
i have two pages.
Index:
const Home = () => (
<div className="container">
<Header />
<main>
Index
</main>
</div>
)
Test:
import Head from 'next/head'
import Header from '../components/header'
const Home = () => (
<div className="container">
<Header />
<main>
Test
</main>
</div>
)
export default Home
I would like to navigate between this pages without losing state i header component. It is possible and how?
One thing you can do is to wrap your entire Next.js app in a layout component which includes the <Header/>. Check out this sandbox I created to see how this pattern can be applied to the example in your question:
https://codesandbox.io/s/so-q-63755826-b-forked-7xt6u
Check out this great article which explains this pattern as well as some other solutions for persisting layout in Next.js:
https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/
Related
i'm trying to create a web app with react redux and router, and i'm getting a wierd behaviour, the Router is rendering only on refresh.
that my App.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Router, Switch, Route, Link } from "react-router-dom";
import { ModalProvider } from './modal/modalContext';
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import Login from "./components/Login";
import Register from "./components/Register";
import Home from "./components/Home";
import Profile from "./components/Profile";
import BoardUser from "./components/BoardUser";
import BoardModerator from "./components/BoardModerator";
import BoardAdmin from "./components/BoardAdmin";
import DataService from "./services/data.service";
import { logout } from "./actions/auth";
import { clearMessage } from "./actions/message";
import { setDc } from "./actions/dc";
import { history } from "./helpers/history";
import simplidc_logo from "./images/dns-24px.svg"
import DetailedRackCard from './components/detailed_rack_card'
const App = () => {
const [showModeratorBoard, setShowModeratorBoard] = useState(false);
const [showAdminBoard, setShowAdminBoard] = useState(false);
const { user: currentUser } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
history.listen((location) => {
dispatch(clearMessage()); // clear message when changing location
});
}, [dispatch]);
useEffect(() => {
if (currentUser) {
setShowModeratorBoard(currentUser.roles.includes("ROLE_MODERATOR"));
setShowAdminBoard(currentUser.roles.includes("ROLE_ADMIN"));
}
}, [currentUser]);
useEffect(() => {
DataService.getDevices().then(
(devicesResponse) =>{
DataService.getRacks().then(
(racksResponse) =>{
dispatch(setDc(DataService.getDc(racksResponse,devicesResponse)));
}
)
}
)
}, [dispatch]);
const logOut = () => {
dispatch(logout());
};
return (
<div>
<ModalProvider>
<Router history={history}>
<div>
<nav className="navbar navbar-expand navbar-dark bg-dark">
<Link to={"/"} className="navbar-brand">
SimpliDC
<img
alt=""
src={simplidc_logo}
width="30"
height="30"
/>
</Link>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/home"} className="nav-link">
Home
</Link>
</li>
{showModeratorBoard && (
<li className="nav-item">
<Link to={"/mod"} className="nav-link">
Moderator Board
</Link>
</li>
)}
{showAdminBoard && (
<li className="nav-item">
<Link to={"/admin"} className="nav-link">
Admin Board
</Link>
</li>
)}
{currentUser && (
<li className="nav-item">
<Link to={"/user"} className="nav-link">
User
</Link>
</li>
)}
</div>
{currentUser ? (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/profile"} className="nav-link">
{currentUser.username}
</Link>
</li>
<li className="nav-item">
<a href="/login" className="nav-link" onClick={logOut}>
LogOut
</a>
</li>
</div>
) : (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/register"} className="nav-link">
Sign Up
</Link>
</li>
</div>
)}
</nav>
<input className="" type="text" placeholder="Type any vaule to search in the DC..."/>
<div className="container mt-3">
<Switch>
<Route exact path={["/", "/home"]} component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
<Route exact path="/profile" component={Profile} />
<Route path="/user" component={BoardUser} />
<Route path="/mod" component={BoardModerator} />
<Route path="/admin" component={BoardAdmin} />
<Route path="/rack/:id" component={DetailedRackCard}/>
</Switch>
</div>
</div>
</Router>
</ModalProvider>
</div>
);
};
export default App;
and i found something wierd happening while looking in the react dev tools on the Router component. this is the location in state after refresh:
and this is the same object after i click a route (login):
i'm desprate for help....
yes, that my only router in the whole app.
thanks!
The state of the <Router/> comes from the history prop which your are providing. You are not modifying that history properly. The bad mutation will be somewhere in another file. It seems like you are calling history.push() with the entire action object instead of just the action.location property.
Using the <Router/> component with a custom history prop is an advanced design pattern that you generally want to avoid unless absolutely necessary. I don't think it's necessary here, at least not for the code in this particular file.
I would recommend using a <BrowserRouter/> (or <HashRouter/>, etc. instead. If you do that, you'll need some other way to address the history.listen subscription (sidenote: you want to use a cleanup function to stop listening when the component unmounts).
You can use a useEffect hook to respond to changes in the location which you access from the useLocation hook.
const location = useLocation();
useEffect(() => {
dispatch(clearMessage()); // clear message when changing location
}, [location, dispatch]);
If you don't want to clear the message on the first render, you can add some additional checks before dispatching. You can potentially use a usePrevious custom hook or something like it. Here I am using useRef to store the previous location. The initial value for the ref is the location from useLocation, so on the first render they will be equal.
const location = useLocation();
const locRef = useRef(location);
useEffect(() => {
console.log(locRef.current, location);
// will be equal on first render
if (locRef.current !== location) {
dispatch(clearMessage());
}
locRef.current = location;
}, [location, dispatch]);
You could modify this to look at just the pathname if you want.
I am adding Vue support to an existing Laravel system. Some of the data fields contain HTML tags.
Rendering it in Laravel was easy with the {!! option.
While passing it to Vue, I am seeing that some of the tags are getting omitted out.
For e.g.
Data passed from Laravel - I have verified this with the dd command in the blade.
<div>
<ul>
<li>List 1</li>
<li>List 2</li>
<li>List 3</li>
</ul>
</div>
gets passed to Vue as below
This is the data received in Vue. I have verified it with console.log and Vue developer tools.
List1</li>List 2</li>List 3</li>
Any pointers on what I could be doing wrong would be great.
I faced the same problem. It's all about XSS vulnerabilities. A workaround could be using slot on vue component and echo rendered view but using props is also possible.
ExampleComponent.vue:
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {};
</script>
Returning rendered view :
Route::get('/test', function () {
return view('test.index', ['view' => view('test.list', ['num' => 5])->render()]);
});
test\index.blade.php
<example-component>
{!!$view!!}
</example-component>
and test\list.blade.php
<div >
<ul>
#for ($i = 0; $i < $num; $i++)
<li>List {{$i+1}}</li>
#endfor
</ul>
</div>
You can also use props but you need v-html directive:
ExampleComponent.vue:
<template>
<div>
<span v-html="view"></span>
</div>
</template>
<script>
export default {
props:['view']};
</script>
test\index.blade.php
<example-component view="{{$view}}">
</example-component>
My case looks like this:
Laravel template:
<div class="block-id" is="Header">
<span>ID #3265872</span>
<ul class="tools">
<li>History</li>
<li>TOP</li>
</ul>
<button>Check</button>
</div>
The vuejs component looks just the same
<template>
<div class="block-id">
<span>ID #{{ids.id}}</span>
<ul class="tools">
<li><a>History</a></li>
<li><a>TOP</a></li>
</ul>
<button>Check</button>
</div>
</template>
<script>
import {mapGetters} from 'vuex';
export default {
name: "Header",
computed: {
...mapGetters('global', [
'ids'
])
}
}
</script>
The problem is that when I render the component the href attribute is gone. So is there any way to preserve the href attribute in a element?
I was trying to create a survey app using React, Redux. While trying to run the program on localhost:3000, I am getting this React.Children.only expected to receive a single React element child error. I have tried using various solutions which programmers have mentioned elsewhere but nothing has worked. The codes are as below. I must admit I am an absolute tyro as far as programming is concerned
App.js:
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Header from './Header';
const Dashboard =() => <h2>Dashboard</h2>;
const SurveyNew =() => <h2>SurveyNew</h2>;
const Landing =() => <h2>Landing</h2>;
const App = () => {
return (
<div>
<BrowserRouter>
<div>
<Header />
<Route exact path= "/" component = {Landing} />
<Route exact path = "/surveys" component= {Dashboard} />
<Route path = "/surveys/new" component={SurveyNew} />
</div>
</BrowserRouter>
</div>
);
};
export default App;
The code of header.js file is as below:
import React, {Component} from 'react';
class Header extends Component {
render () {
return (
<div>
<nav>
<div className = "nav-wrapper">
<a className = "left brand-logo">
Emaily
</a>
<ul className="right">
<li>
<a> Login with Google </a>
</li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Header;
I have a vue js application that fetches data from a database using ajax and presents them in a foundation tab. I have emulated everything as best I know how but unfortunately the content is not appearing.
My Vue component code is as below.
<template>
<section>
<pos-header></pos-header>
<div id="products">
<ul class="tabs" data-tabs id="product-types">
<li class="tabs-title" v-for="type, index in products.types" :class="{ 'is-active': index == 0}">
<a :href="'#' + type.name">
{{ type.name }}
</a>
</li>
</ul>
<div class="tabs-content" data-tabs-content="product-types">
<div v-for="type, index in products.types" class="tabs-panel" :class="{ 'is-active': index == 0}" :id="type.name">
<p>Products</p>
<p>{{ index }} {{ type }}</p>
</div>
</div>
</div>
</section>
</template>
<script>
import { mapActions } from 'vuex';
import Header from './includes/Header.vue';
export default{
data() {
return {
products: {
types: []
}
}
},
components: {
'pos-header': Header,
},
mounted(){
let url = this.$store.getters.getApiUrl + '/product/types';
axios.get(url).then( response => {
this.products.types = response.data.productTypes;
$(document).foundation();
});
}
}
</script>
The mounted method fetches the data and saves them in a data property called products types and I have ascertained that the items are being retrieved. This application is contained within a laravel application and laravel handles the backend api.
I should also add that i am able to see the links that change the tab content but the contents of the tabs are not displayed.