React Router renders only after refresh - react-redux

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.

Related

NextJS - keep header and footer on page [duplicate]

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/

JSX how to return an image correctly? Üebersicht widget

I would like to return an image as a widget.
I put my photo.png in a folder images next to myFile.jsx.
I have always the same error :
Can't walk dependency graph: Cannot find module './images/photo.png' form /Users/macbookpro/Library/Application Support/Übersicht/widgets/my file/myFile.jsx required by /Users/macbookpro/Library/Application Support/Übersicht/widgets/my file/myFile.jsx
I have also tried to: move the photo to my Desktop, use require, use import .. from ...
Here there is the code:
import { css } from "uebersicht";
// import photo from "./images/photo.jpg";
// import photo from "./Users/macbookpro/Desktop/photo.png";
export const render = () => {
return (
<div>
<img src="./images/photo.png" />
{/* <img src="./Users/macbookpro/Desktop/photo.png" /> */}
{/* <img src={require("./images/photo.png")} /> */}
{/* <img src={require("./Users/macbookpro/Desktop/photo.png")} /> */}
</div>
);
};
Since myFile.jsx is actually act as a component for uebersicht you have to include your widget folder name in you src.
For example if your photo.png is in example.widget/images folder, your should be:
<img src="example.widget/images/photo.png/>

react-children expect to receive a single react element child error

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;

how to require images that are passed in as prop names into react component?

Lets say I have a bunch of local images that are part of my react project. I have a component that accepts props from higher up -- and one of the props is image name. So using , this could work. The tricky part is that I am using webpack, and I think thats why my images dont get displayed -- they arent pulled in.
So before I had converted my component to use props, the image name was hardcoded, and worked like this:
<img src={require('../images/products/image-aqua.png')}/>
but now it looks as following, and doesnt work:
<img src={this.props.productImageUrl} />
Essentially, I am trying to stradle react "on the fly" image names and webpack packing concepts (I do know about webpack url-loader and file-loader)
thank you
You should probably post your webpack so we can help you further. Your code above should work no problem.
Here is my webpack for img and such and it works as you are trying to use it.
{
test: /\.(jpg|jpeg|gif|png|tiff|svg)$/,
exclude: /\.glyph.svg/,
use: [
{
loader: 'url-loader',
options: {
limit: 6000,
name: 'image/[name].[ext]',
},
},
],
},
The only other issue you could be encountering is that your images aren't actually loaded when your component loads. Are you getting any errors or they just aren't showing?
class ProductList extends React.Component {
render() {
const product = Seed.products[0];
return (
<div className='ui unstackable items'>
<Product
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitterAvatarUrl={`${product.submitterAvatarUrl}`}
productImageUrl={`${product.productImageUrl}`}
/>
</div>
);
}
}
class Product extends React.Component {
render() {
console.log("image name " + `${this.props.productImageUrl}`);
return (
<div className='item'>
<div className='image'>
<img src={`${this.props.productImageUrl}`} /> {/*this.props.productImageUrl*/}
</div>
<div className='middle aligned content'>
<div className='header'>
<a>
<i className='large caret up icon' />
</a>
{this.props.votes}
</div>
<div className='description'>
<a href={this.props.url}>
{this.props.title}
</a>
<p>
{this.props.description}
</p>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src={`${this.props.submitterAvatarUrl}`}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<ProductList />,
document.getElementById('content')
);
...
import aqua from '../images/products/image-aqua.png';
import daniel from '../images/avatars/daniel.jpg';
export default window.Seed = (function () {
function generateVoteCount() {
return Math.floor((Math.random() * 50) + 15);
}
const products = [
{
id: 1,
title: 'Yellow Pail',
description: 'On-demand sand castle construction expertise.',
url: '#',
votes: generateVoteCount(),
submitterAvatarUrl: daniel,
productImageUrl: aqua,
},

Component function as Ajax callback

I have a menu component that use Ajax call for dynamic HTML rendering, so the browser won't refresh when the user click on one of the menu item. We use jQuery-ajax-unobtrusive to perform the Ajax call. This part works well.
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<!-- Some HTML ... -->
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>
<a href="/Home/Contact"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-success="setUrl"
data-ajax-failure="redirectToLogin"
data-ajax-update="#renderbody">Contact</a>
</li>
</ul>
</div>
</div>
</div>
And I use Ajax callback like this
data-ajax-success="setUrl"
data-ajax-failure="redirectToLogin"
I was wondering if these callbacks can be encapsulated in Angular2' component functions written in Typescript ? For the moment, I have a Javascript error telling that it cannot found methods 'setUrl' and 'redirectToLogin'.
Here is a sample implementation of this within a custom Angular2 directive:
#Directive({
selector: '[data-ajax]',
host: {
'(click)': 'onClick($event)'
}
})
export class DataAjaxDirective {
#Input('href')
url:string;
#Input('data-ajax')
enabled:boolean;
#Input('dataAjaxMethod')
method:string;
#Input('dataAjaxMode')
mode:string;
#Output('data-ajax-success')
success:EventEmitter<any> = new EventEmitter();
#Output('data-ajax-failure')
failure:EventEmitter<any> = new EventEmitter();
constructor(private http:Http) {
}
onClick(event) {
event.preventDefault();
event.stopPropagation();
this.http[this.method.toLowerCase()](this.url).map(res => res.json()).subscribe(
(data) => {
this.success.emit(data);
},
(err) => {
this.failure.emit(err);
}
);
}
}
And the way to use it:
#Component({
selector: 'my-app',
template: `
<div>
<a href="https://mapapi.apispark.net/v1/maps/"
data-ajax="true"
dataAjaxMethod="GET"
dataAjaxMode="replace"
(data-ajax-success)="onDataReceived($event)"
(data-ajax-failure)="redirectToLogin()"
data-ajax-update="#renderbody">Contact</a>
<ul>
<li *ngFor="#map of maps">{{map.name}}</li>
</ul>
</div>
`,
directives: [ DataAjaxDirective ]
})
export class AppComponent {
onDataReceived(data) {
this.maps = data;
}
}
See this plunkr: https://plnkr.co/edit/Csw9bq7Ufa3bGXkqpSyN?p=preview.
JQuery needs global setUrl function: window.setUrl = function(){}; which is not way to go.
You could write your own implementation of jquery.unobtrusive-ajax.js using custom Directive.

Resources