React useTranslation alternative in Class Component - reactjs

I am a total beginner in React (as I am in StackOverflow, sorry if the form of my post isn't quite right)
I have this SPA app that translates my content from french to english and back, works perfectly fine, but I would like to add an "active" className to my language toggle buttons so I can underline the active language in css, and what I found I had to do was dealing with the state of the component. Now I use a function component with useTranslation, and if I try and convert my function component to a class component, it says useTranslation can't be used in a class component.
Could any of you help me re-write my component so I can make it a class component and use a constructor and deal with the states, but still use the same hooks and functions I'm using now for translation?
I found previous aswers using withTranslation wrapper, but no example that would take my toggle buttons and changeLanguage function for navigation into account, just for {t} plain strings, so I'im a bit lost on how to write this correctly...
Here is my app code as a function component:
import React, { Component } from "react";
import {
Route,
NavLink,
HashRouter
} from "react-router-dom";
import Home from "./Home";
import Projets from "./Projets";
import Contact from "./Contact";
import Design from "./Design";
import Web from "./Web";
import ReactDOM from "react-dom";
import { useTranslation, Trans } from "react-i18next";
export default function App() {
const { t, i18n } = useTranslation();
const changeLanguage = lng => {
i18n.changeLanguage(lng);
};
return (
<div className="App">
<div className="App-header">
<h1>{t("Bienvenue sur notre Blog")}</h1>
</div>
<HashRouter>
<div className="main-container">
<div className="navigation">
<ul className="nav-list">
<li className="nav-links"><NavLink exact to="/"><Trans>Accueil</Trans></NavLink></li>
<li className="nav-links"><NavLink to="/projets"><Trans>Projets</Trans></NavLink></li>
<ul className="nav-sublist">
<li className="nav-sublinks"><NavLink to="/projets/graphisme-maquettes"><Trans>Graphisme</Trans></NavLink></li>
<li className="nav-sublinks"><NavLink to="/projets/web"><Trans>Web</Trans></NavLink></li>
</ul>
<li className="nav-links"><NavLink to="/contact"><Trans>Contact</Trans></NavLink></li>
</ul>
<div class='langSelect'>
<button onClick={() => changeLanguage("fr")} class='langBtn' value='lang'>fr</button>
<button onClick={() => changeLanguage("en")} class='langBtn' value='lang'>en</button>
</div>
</div>
<div className="content">
<Route exact path="/" component={Home}/>
<Route path="/projets" component={Projets}/>
<Route path="/projets/graphisme-maquettes" component={Design}/>
<Route path="/projets/web" component={Web}/>
<Route path="/contact" component={Contact}/>
</div>
</div>
</HashRouter>
</div>
);
}
And here is my i18n.js file:
import i18n from "i18next";
// import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
i18n
// .use(LanguageDetector)
.use(initReactI18next)
.use(LanguageDetector)
.init({
// we init with resources
resources: {
en: {
translations: {
Accueil:"Home",
Projets:"Work",
Graphisme:"Design",
Web: "Web",
Contact:"Contact",
// "Projets":"Work",
"Bienvenue sur notre Blog":"Welcome to our Blog"
}
},
fr: {
translations: {
Accueil:"Accueil",
Projets:"Projets",
Graphisme:"Graphisme",
Web: "Web",
Contact:"Contact",
// "Projets":"Projets",
"Bienvenue sur notre Blog":"Bienvenue sur notre Blog"
}
}
},
fallbackLng: "fr",
debug: true,
// have a common namespace used around the full app
ns: ["translations"],
defaultNS: "translations",
keySeparator: false, // we use content as keys
interpolation: {
escapeValue: false
}
});
export default i18n;
Now, what I would like to achieve is something like this:
import React, { Component } from "react";
import {
Route,
NavLink,
HashRouter
} from "react-router-dom";
import Home from "./Home";
import Projets from "./Projets";
import Contact from "./Contact";
import Design from "./Design";
import Web from "./Web";
import ReactDOM from "react-dom";
import { useTranslation, Trans } from "react-i18next";
class App extends Component {
constructor(){
super();
this.state = {
underline: false
}
}
render {
const { t, i18n } = useTranslation(); => OBVIOULSY USE SOMETHING ELSE
const changeLanguage = lng => {
i18n.changeLanguage(lng);
}; => ADD/REMOVE THE CLASSNAME HERE AT THE SAME TIME I CHANGE LANGUAGE?
return (
<div className="App">
<div className="App-header">
<h1>{t("Bienvenue sur notre Blog")}</h1>
</div>
<HashRouter>
<div className="main-container">
<div className="navigation">
<ul className="nav-list">
<li className="nav-links"><NavLink exact to="/"><Trans>Accueil</Trans></NavLink></li>
<li className="nav-links"><NavLink to="/projets"><Trans>Projets</Trans></NavLink></li>
<ul className="nav-sublist">
<li className="nav-sublinks"><NavLink to="/projets/graphisme-maquettes"><Trans>Graphisme</Trans></NavLink></li>
<li className="nav-sublinks"><NavLink to="/projets/web"><Trans>Web</Trans></NavLink></li>
</ul>
<li className="nav-links"><NavLink to="/contact"><Trans>Contact</Trans></NavLink></li>
</ul>
<div class='langSelect'>
<button onClick={() => changeLanguage("fr")} class='langBtn' value='lang'>fr</button>
<button onClick={() => changeLanguage("en")} class='langBtn' value='lang'>en</button>
</div>
</div>
<div className="content">
<Route exact path="/" component={Home}/>
<Route path="/projets" component={Projets}/>
<Route path="/projets/graphisme-maquettes" component={Design}/>
<Route path="/projets/web" component={Web}/>
<Route path="/contact" component={Contact}/>
</div>
</div>
</HashRouter>
</div>
);
}
}
export default App;
Thanks in advance, I hope you have everything your need, I really "just" need guidance in how to write all of this without changing the way it works so much, I hope it's actually doable...

Related

React & Typescript Issue: trigger elements with InsertionObserver using props and manage them in other component

Small premise: I'm not a great Typescript expert
Hi everyone, I'm working on my personal site, I decided to develop it in Typescript to learn the language.
My component tree is composed, as usual, of App.tsx which render the sub-components, in this case Navbar.jsx and Home.jsx.
Below is the App.jsx code:
import './App.css';
import { BrowserRouter as Router, useRoutes } from 'react-router-dom';
import Home from './components/Home';
import Navbar from './components/Navbar';
import { useState } from 'react';
function App(){
const [navbarScroll,setNavbarScrool]=useState(Object)
const handleLocationChange = (navbarScroll : boolean) => {
setNavbarScrool(navbarScroll)
return navbarScroll
}
const AppRoutes = () => {
let routes = useRoutes([
{ path: "/", element: <Home handleLocationChange={handleLocationChange}/> },
{ path: "component2", element: <></> },
]);
return routes;
};
return (
<Router>
<Navbar navbarScroll={navbarScroll}/>
<AppRoutes/>
</Router>
);
}
export default App;
Here, instead, the Home.jsx code:
import { useInView } from 'react-intersection-observer';
import HomeCSS from "../styles/home.module.css"
import mePhoto from "../assets/me.png"
import { useEffect, useState } from 'react';
interface AppProps {
handleLocationChange: (values: any) => boolean;
}
export default function Home(props: AppProps){
const { ref: containerChange , inView: containerChangeIsVisible, entry} = useInView();
useEffect(()=>{
props.handleLocationChange(containerChangeIsVisible)
//returns false at first render as expected
console.log("Home "+containerChangeIsVisible)
},[])
return(
<>
<div className={`${ HomeCSS.container} ${containerChangeIsVisible? HomeCSS.container_variation: ''}`}>
<div className={HomeCSS.container__children}>
{/* when i scroll on the div the css change (this works)*/}
<h1 className={`${ HomeCSS.container__h1} ${containerChangeIsVisible? HomeCSS.container__h1_variation: ''}`}>My<br/> Name</h1>
<p>Computer Science student.</p>
</div>
<img src={mePhoto} className={HomeCSS.image_style}/>
</div>
<div ref={containerChange} style={{height:800,background:"orange"}}>
<p style={{marginTop:20}}>HIII</p>
</div>
</>
)
}
And Navbar.jsx:
import NavbarCSS from "../styles/navbar.module.css"
import acPhoto from "../assets/ac.png"
import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
interface NavbarScroolProp{
navbarScroll:boolean
}
export default function Navbar(props:NavbarScroolProp){
const [scrollState,setScrollState]=useState(false)
const [pVisible,setpVisible] = useState('')
useEffect(()=>{
setTimeout(() => {
setpVisible("")
}, 3000)
setpVisible("100%")
},[])
//returns false also when should be true
console.log(props.navbarScroll)
return (
<>
{/*the props is undefined so the css doesn't change, i need to do this*/}
<nav className={`${props.navbarScroll?NavbarCSS.nav__variation:NavbarCSS.nav}`}>
<div className={NavbarCSS.nav_row}>
<div className={NavbarCSS.nav_row_container}>
<img src={acPhoto} className={NavbarCSS.image_style}/>
<p className={NavbarCSS.p_style} style={{maxWidth: pVisible}}>My name</p>
</div>
<div className={NavbarCSS.nav_row_tagcontainer}>
<Link className={NavbarCSS.nav_row_tag} to="/"> Home</Link>
<Link className={NavbarCSS.nav_row_tag} to="/"> About</Link>
<Link className={NavbarCSS.nav_row_tag} to="/"> Contact</Link>
</div>
</div>
</nav>
</>
);
}
In my application I want to change the background color whenever the div referring to the InsertionObserver ( I use "useInView" hook , from :https://github.com/thebuilder/react-intersection-observer) is displayed. The problem is that the div in question is in the Home.jsx component and I need to change the color of the divs in the navbar as well when the div in Home is triggered(or other components in case I need to in the future).
The question is: How can I dynamically trigger DOM elements of other components (to then perform certain operations) using the InsertionObserver ?
As you can see from the code I tried to create Props, but everything returns undefined and doesn't involve any changes.
I've tried without useEffect, without using the useInView hook, passing the object instead of the boolean value, but I can't find any solutions to this problem.
You would be of great help to me.
PS: I would like to leave the Navbar.jsx component where it is now, so that it is visible in all components.
Any advice or constructive criticism is welcome.

How to pass state to components rendered by Router

I am making a React portfolio and one of the components will be an image gallery (needless to say I am a newbie to React, I wonder, or I'd not be asking this question). To do so, I will use a package (react-image-gallery), which allows me to customize several items. I already installed, and it works well, but I need to customize it - hide thumbnails and "play" and "fullscreen" buttons.
According to the documentation, it is possible to do so via the state. The issue is that I am failing to do so because the navigation of the App uses a router, and although I tried hard, I could not make the state pass into the component placed on Router. This is the code of the main component (the equivalent to App) which is not working:
import "./styles.css";
import {
HashRouter,
NavLink,
Route
} from "react-router-dom";
import React, { Component } from "react";
import Development from "./Development";
import Home from "./Home";
import Intro from "./Intro";
import Media from "./Media";
import StudyCase from './StudyCase';
import Stuff from "./Stuff";
class Main extends Component {
constructor() {
super();
this.state = {
showThumbnails: false
};
}
render() {
return (
<HashRouter>
<div>
<ul className='top-menu'>
<li>
<NavLink className='navs hvr-pulse' to='/'>
Home
</NavLink>
</li>
<li>
<NavLink className='navs hvr-pulse' to='/dev'>
Development
</NavLink>
</li>
<li>
<NavLink className='navs hvr-pulse' to='/media'>
Media
</NavLink>
</li>
<li>
<NavLink className='navs' to='/study-case'>
Study Case
</NavLink>
</li>
</ul>
<span className='bottom-slider'></span>
<div className='content'>
<Route exact path='/' component={Intro} />
<Route path='/dev' component={Development} />
<Route path='/media' component={Media} />
<Route path='/study-case' render={props => (<StudyCase {...this.state} showThumbnails={this.state.showThumbnails}/>)}/>
</div>
</div>
</HashRouter>
);
}
}
export default Main;
Can some of the colleagues point the error I am doing to me? Thanks in advance for the availability.
After some hours looking for the solution, I nailed it, and it was pretty basic, so a newbie could find the answer. It happened that the <StudyCase/> component had a component itself, and the state should live there. I was led to error by the rationale of a gallery package I was working with before, where state had to be manipulated in the same page of the router. Here is the code of the <StudyCase/> component, with the working state, if someone may need at some point:
import ImageGallery from 'react-image-gallery';
import React from 'react';
class StudyCase extends React.Component {
constructor() {
super();
this.state = {
showThumbnails: false,
showPlayButton: false,
showFullscreenButton: false,
showGalleryFullscreenButton: false,
};
}
render() {
return (
<ImageGallery
items={images}
showThumbnails={this.state.showThumbnails}
showPlayButton={this.state.showPlayButton && this.state.showGalleryPlayButton}
showFullscreenButton={
this.state.showFullscreenButton && this.state.showGalleryFullscreenButton
}
/>
// <ImageGallery
// items={images}
// showThumbnails={this.state.showThumbnails} />;
);
}
}
const images = [
{
original: 'https://picsum.photos/id/1018/1000/600/',
thumbnail: 'https://picsum.photos/id/1018/250/150/',
},
{
original: 'https://picsum.photos/id/1015/1000/600/',
thumbnail: 'https://picsum.photos/id/1015/250/150/',
},
{
original: 'https://picsum.photos/id/1019/1000/600/',
thumbnail: 'https://picsum.photos/id/1019/250/150/',
},
];
export default StudyCase;

React and i18n - translate by adding the locale in the URL

Hello I'm building a demo application just to learn React and I'm kinda stuck with the translation proccess. What I'm trying to do is have a multi-language website with default language the "Greek" and secondary "English". When Greek are enabled the URL shouldn't contain any locale in the URL but when English are, the URLS should be rewritten with /en/.
i18n Config
import translationEn from './locales/en/translation';
import translationEl from './locales/el/translation';
import Constants from './Utility/Constants';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'el',
debug: true,
ns: ['translations'],
defaultNS: 'translations',
detection: {
order: ['path'],
lookupFromPathIndex: 0,
},
resources: {
el: {
translations: translationEl
},
en: {
translations: translationEn
}
},
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
}
}, () => {
// Why the fuck this doesnt work automatically with fallbackLng ??
if (!Constants.allowed_locales.includes(i18n.language)) {
i18n.changeLanguage(Constants.default_locale);
}
return;
});
i18n.on('languageChanged', function (lng) {
// if somehow it get injected
if (!Constants.allowed_locales.includes(i18n.language)) {
i18n.changeLanguage(Constants.default_locale);
}
// if the language we switched to is the default language we need to remove the /en from URL
if (Constants.default_locale === lng) {
Constants.allowed_locales.map((item) => {
if (window.location.pathname.includes("/" + item)) {
let newUrl = window.location.pathname.replace("/" + item, "");
window.location.replace(newUrl);
}
})
} else { // Add the /en in the URL
// #todo: add elseif for more than 2 langs because this works only for default + 1 more language
let newUrl = "/" + lng + window.location.pathname;
window.location.replace(newUrl);
}
});
export default i18n;
I used the Language Detector plugin with detection from path so it can parse the locale from URL. Now without the callback function I added at the initialization, the LanguageDetector would set correctly the language if the url was www.example.com/en/ or www.example.com/el or www.example.com/en/company. BUT if I directly accessed the www.example.com/company (before visiting first the home so the locale would be set) i18n would set the locale/language to "company" !!!
There is an option for fallbackLng that I thought that would set the language to what you config it if the LanguageDetector dont detect it, but seems that there isnt an option to set available languages or default language to i18n ( or I'm an idiot and couldnt find it ) so LanguageDetector set whatever he finds in the URL. To fix this I added a Constants file and the callback function above.
Contants.js
const Constants = {
"allowed_locales": ['el','en'],
"default_locale": 'el'
}
export default Constants;
Also I added an event Handler that fires on LanguageChange so it will rewrite the URL with /en/ if English is active or remove the /el/ if Greek is.
index.js
ReactDOM.render(
<BrowserRouter>
<I18nextProvider i18n={i18n}>
<App/>
</I18nextProvider>
</BrowserRouter>
,
document.getElementById('root')
);
App.js
class App extends React.Component {
render() {
return (
<div className="App">
<Suspense fallback="loading">
<Header {...this.props}/>
<Routes {...this.props} />
</Suspense>
</div>
);
}
}
export default withTranslation('translations')(App);
Nothing special for index.js and App.js
Header Component
class Header extends React.Component {
linkGenerator(link) {
// if the current language is the default language dont add the lang prefix
const languageLocale = this.props.i18n.options.fallbackLng[0] === this.props.i18n.language ? null : this.props.i18n.language;
return languageLocale ? "/" + languageLocale + link : link;
}
render() {
return (
<div className="header">
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand className="logo" href="/"> <Image src="/assets/logo.png" rounded/>
</Navbar.Brand>
{/*Used For Mobile Navigation*/}
<Navbar.Toggle aria-controls="basic-navbar-nav"/>
<Navbar.Collapse id="basic-navbar-nav" className="float-right">
<Nav className="ml-auto">
<Nav.Link as={NavLink} exact to={this.linkGenerator("/")}>{this.props.t('menu.home')}</Nav.Link>
<Nav.Link as={NavLink} to={this.linkGenerator("/company")}>{this.props.t('menu.company')}</Nav.Link>
</Nav>
<Nav className="mr-auto">
{this.props.i18n.language !== "el" ? <button onClick={() => this.props.i18n.changeLanguage('el')}>gr</button>
: null}
{this.props.i18n.language !== "en" ? <button onClick={() => this.props.i18n.changeLanguage('en')}>en</button>
: null}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
)
}
}
export default Header
In order to create the urls of the Menu with the locale, I created the linkGenerator function
And Finally in my Routes Component which handle all the routing, I added a constant before the actual url so it will work for all of theese /page , /el/page , /en/page
Routes Component
import React from 'react';
import {Switch, Route} from 'react-router-dom';
import CompanyPage from './Pages/CompanyPage';
import HomePage from './Pages/HomePage';
import NotFound from './Pages/NotFound';
class Routes extends React.Component {
render() {
const localesString = "/:locale(el|en)?";
return (
<Switch>
<Route exact path={localesString + "/"} component={HomePage}/>
<Route path={localesString + "/company"} component={CompanyPage}/>
<Route component={NotFound}/>
</Switch>
);
}
}
export default Routes
The code somehow works but is full of hacks like :
Extra config file ( constants.js )
Callback function to change the language from "company" to default locale. ( this triggers 2 page reloads)
functions to handle the locale in the menu and routes
etc..
Isnt there any "build-in" functionality or a better approach in order to achieve the same thing without the above hacks?
I needed the same thing and I found out that you can set the whitelistproperty of i18n.init options and specify the supported languages. After that, if you set checkWhitelist: true inside your detection options, the LanguageDetector will only match the language if it exists on the whitelist array.
Anyway, you still need to define the languageChanged event in order to redirect the page when matching the default language but, you no longer need to redirect if it is another supported language (at least I don't need).
Last thing that I did differently is that I defined the languageChanged event first and only then called the i18n.init, so that it would trigger the event already for the first time that it sets the language.
Here's my code:
i18n.js
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
i18n.on('languageChanged', function (lng) {
// if the language we switched to is the default language we need to remove the /en from URL
if (lng === i18n.options.fallbackLng[0]) {
if (window.location.pathname.includes('/' + i18n.options.fallbackLng[0])) {
const newUrl = window.location.pathname.replace('/' + i18n.options.fallbackLng[0], '')
window.location.replace(newUrl)
}
}
})
i18n
.use(LanguageDetector)
.init({
resources: {
en: {
translation: require('./translations/en.js').default
},
pt: {
translation: require('./translations/pt.js').default
}
},
whitelist: ['en', 'pt'],
fallbackLng: ['en'],
detection: {
order: ['path'],
lookupFromPathIndex: 0,
checkWhitelist: true
},
interpolation: {
escapeValue: false,
formatSeparator: '.'
}
})
export default i18n
App.js
import { Route, Switch } from "react-router-dom";
import AboutPage from "./AboutPage";
import HomePage from "./Homepage/HomePage";
import NotFoundPage from "./NotFoundPage";
import PropTypes from "prop-types";
import React from "react";
import { hot } from "react-hot-loader";
import {
Collapse,
Navbar,
NavbarToggler,
NavbarBrand,
Nav,
NavItem,
NavLink } from 'reactstrap';
import i18n from "../i18n";
const baseRouteUrl = "/:locale(pt|en)?";
export const baseUrl = i18n.language === 'en' ? '' : '/'+i18n.language;
class App extends React.Component {
state = {
isOpen: false
}
render() {
return (
<div>
<div>
<Navbar color="grey" expand="md">
<NavbarBrand href="/">Testing</NavbarBrand>
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink href={baseUrl + "/"}>Home</NavLink>
</NavItem>
<NavItem>
<NavLink href={baseUrl + "/about/"}>About</NavLink>
</NavItem>
</Nav>
</Navbar>
</div>
<Switch>
<Route exact path={baseRouteUrl + "/"} component={HomePage} />
<Route path={baseRouteUrl + "/about"} component={AboutPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
);
}
}
App.propTypes = {
children: PropTypes.element
};
export default hot(module)(App);
In my case, when I need to translate something, I import my i18n.js and call the respective key like this:
<div>{i18n.t('home.bannerStart')}</div>
When you define a basename for the Router you don't need to add to every Route.
// in your case use el instead of en
export const baseUrl = () => return (i18n.language === "en" ? "" : "/" + i18n.language);
And the router should be something like this:
<Router basename={baseUrl()}>
...
</Router>
You can call the baseUrl() whenever you need to create a link. Also note that <Link> already takes the basename into regard, so you do not have to define it.
For the language change check my other answer here

Passing data in store from one component to another on different urls using redux, react-router?

I am having some trouble accessing what I wrote to the store after one of my actions fires and reading it on a different page and different component via react-router. Here is what my main code looks like:
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={LetterForm}></IndexRoute>
<Route path="/preview" component={Preview}></Route>
</Route>
</Router>
</Provider>
And so on the Index page the user fills out a form and the submit button for that form fires an action that saves all the form data into the store and brings them to /preview. However when I get the the Preview component and try to access the data I just stored it is nowhere to be found? How can I make it so that when the user fills out a form on the Index page, I can display their results on a different one.
Edit: Here are is the code for all my main components
import React from 'react';
import { render } from 'react-dom';
import css from './styles/main.css';
import bootstrap from './styles/bootstrap.css';
import App from './components/App';
import LetterForm from './components/LetterForm';
import Preview from './components/Preview';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import store, {history} from './store';
const router = (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={LetterForm}></IndexRoute>
<Route path="/preview" component={Preview}></Route>
</Route>
</Router>
</Provider>
);
render(router, document.getElementById('root'));
App.js (submitLetter should contain all the form data, after the form is submitted)
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
import Main from './Main';
function mapStateToProps(state) {
return {
submitLocations: state.submitLocations,
submitLetter: state.submitLetter
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const App = connect(mapStateToProps, mapDispatchToProps)(Main);
export default App;
Main.js
import React from 'react';
import { Link } from 'react-router';
import SubmitTo from './SubmitTo';
import LetterForm from './LetterForm';
class Main extends React.Component{
constructor(props){
super(props);
}
render(){
return (
<div className="main-wrapper">
<nav className="navbar navbar-default">
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand" href="#">Logo Ipsum</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
<li>Link</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li>Home</li>
<li>About</li>
<li>Sign up</li>
</ul>
</div>
</div>
</nav>
<div className="main-container">
<h1>
Pitch your startup to the top tech sites, free!
</h1>
<SubmitTo {...this.props}/>
{React.cloneElement(this.props.children,this.props)}
</div>
</div>
)
}
}
export default Main;
LetterForm.js
import React from 'react';
import { Link } from 'react-router';
class LetterForm extends React.Component{
constructor(props){
super(props);
this.state = {'companyInput':'','raisedInput':'','raiseLocation':''};
}
handleSubmit(){
var company = this.refs.company.value;
var companyType = this.refs.companyType.value;
var raised = this.refs.raised.value;
var raiseLocation = this.refs.raiseLocation.value;
var stateCopy = Object.assign({},this.state);
if(!company){
stateCopy['companyInput'] = 'input-error';
}else{
stateCopy['companyInput'] = '';
}
if(!raised){
stateCopy['raisedInput'] = 'input-error';
}else{
stateCopy['raisedInput'] = '';
}
if(!raiseLocation){
stateCopy['raiseLocation'] = 'input-error';
}else{
stateCopy['raiseLocation'] = '';
}
this.setState(stateCopy);
this.props.submitLetter(company,companyType,raised,raiseLocation);
}
render(){
return (
<div className="letter-form well">
<form ref="letterForm">
<h2>Dear TechCrunch Editor,</h2>
<br/>
<p>I am working with a company called</p>
<input ref="company" className={"flat-input big-input "+this.state.companyInput}/>
<br/>
<p>We are:</p>
<label className="letter-label">Bootstrapped</label><input value="bootstrapped" type="radio" ref="companyType" name="companyType"/>
<label className="letter-label">Venture Backed</label><input value="venture" type="radio" ref="companyType" name="companyType"/>
<br/>
<p>and have just raised</p>
<input ref="raised" type="number" className={"flat-input small-input "+this.state.raisedInput}/>
<p>from</p>
<input ref="raiseLocation" className={"flat-input medium-input "+this.state.raiseLocation}/>
<br/>
<p>Extra text</p><br/>
<p>Extra text</p><br/>
<p>Extra text</p>
</form>
<button className="btn btn-success" onClick={this.handleSubmit.bind(this)}>Next step</button>
</div>
)
}
}
export default LetterForm;
Instead of using the Preview component in the route you should use a Container component like say PreviewContainer which encapsulates the Preview Component and gives it access to the form data from the redux state via props (using mapStateToProps). Then in the Preview component just use the form data via props. For example something like this.props.formData.

can't route successfully between components in react app

I have a static react app (that means there is not server side rendering) located under example.com/web-class.gr. My problem is that I can't route between components when I use my sidebar menu.
For instance. When I navigate to example.com/web-class.gr/css-intermediate the page loads as expected. From now on if I navigate to different lessonName the page is loading as expected. But I also have exercises, which I can't load when I press the corresponding button in my menu. To get an idea this is my index.js file:
import React from 'react';
import { Link as ReactLink } from 'react-router';
import sidebarStore from './Sidebar/SidebarStore';
import lessonValues from '../../lessonValues';
import LessonStore from '../../LessonStore';
import SidebarLink from './Sidebar/SidebarLink';
export default class Sidebar extends React.Component {
constructor() {
super();
this.state = {
SidebarIsCollapse: sidebarStore.getCurrentState()
}
this.NavMdPlaceholderClass = 'hidden-xs col-sm-4 col-md-3 col-lg-3';
}
componentWillMount() {
sidebarStore.on('change', () => {
this.setState({ SidebarIsCollapse: sidebarStore.getCurrentState() });
this.ChangeSidebarState();
});
this.RenderMainMenu();
}
ChangeSidebarState() {
const NAV_DefaultClasses = "col-sm-4 col-md-3 col-lg-3 ";
if (this.state.SidebarIsCollapse) {
this.NavMdPlaceholderClass = NAV_DefaultClasses + "slideInLeft";
} else {
this.NavMdPlaceholderClass = NAV_DefaultClasses + "slideOffLeft";
}
}
RenderMainMenu() {
this.main_menu = [];
for (let link of lessonValues) {
let { Id, url, isExercise, title } = link;
this.main_menu.push(<SidebarLink key={Id} url={url} isExercise={isExercise} title={title}/>);
}
}
render() {
return (
<div class={this.NavMdPlaceholderClass} id="nav-md-placeholder">
<nav id="sidebar">
<ul id="main-menu">
<li class="ripple-btn">
<ReactLink to="/" onClick={this.SetLessonDetails.bind(this)}>
<span class="item-align-fix">
<i class="glyphicon glyphicon-home" style={{'marginRight': '10px'}}></i>
<strong>
<span>AΡΧΙΚΗ</span>
</strong>
</span>
</ReactLink>
</li>
{this.main_menu}
</ul>
</nav>
</div>
);
}
}
here is the SidebarLink component file:
import React from 'react';
import LessonStore from '../../../LessonStore';
import { Link as ReactLink } from 'react-router';
export default class SidebarLink extends React.Component {
SetPageTitle() {
LessonStore.setLesson(this.props.url);
}
render() {
let glyphoconType = 'glyphicon ';
glyphoconType += this.props.isExercise ? 'glyphicon-pencil' : 'glyphicon-ok-sign';
glyphoconType += ' nav-ico untaken-lesson';
return (
<li class="ripple-btn">
<ReactLink to={this.props.url} onClick={() => this.SetPageTitle()} >
<span class="item-align-fix">
<i class={glyphoconType}></i>
<span>{this.props.title}</span>
</span>
</ReactLink>
</li>
);
}
}
But if I refresh the page manually, I am able to reveal the exercise page. But now I can't navigate to any other element. Only if I click it in sidebar menu and manually refresh the page.
To sum up:
The lessons are loading dynamically. I can navigate between them.
I can't navigate to exercises. Only if I click the corresponding exercise and hit the refresh button.
If I'm viewing an exercise (eg exercise-1), I am not able to navigate to any other component.
I use nginx and below is my rule for the project:
location ^~ /web-class.gr/ {
try_files $uri $uri/ =404;
if (!-e $request_filename){
rewrite ^(.*)$ /web-class.gr/index.html break;
}
}
And lastly here is my sidebar component:
import React from 'react';
import { Link as ReactLink } from 'react-router';
import sidebarStore from './Sidebar/SidebarStore';
import lessonValues from '../../lessonValues';
import LessonStore from '../../LessonStore';
import SidebarLink from './Sidebar/SidebarLink';
// some other helper functions here
render() {
return (
<div class={this.NavMdPlaceholderClass} id="nav-md-placeholder">
<nav id="sidebar">
<ul id="main-menu">
<li class="ripple-btn">
<ReactLink to="/web-class.gr/" onClick={this.SetLessonDetails.bind(this)}>
<span class="item-align-fix">
<i class="glyphicon glyphicon-home" style={{'marginRight': '10px'}}></i>
<strong>
<span>AΡΧΙΚΗ</span>
</strong>
</span>
</ReactLink>
</li>
{this.main_menu}
</ul>
</nav>
</div>
);
Is there any problem with ReactLink to? On my apache machine all works as expected. I can't figure out why my program breaks.
Update
I provide the link of the site to help your job become easier. The site is in greek although I believe you can understand it's structure.
web-class.gr
Code on Github
This One worked For me
NB
for React Router
use <BrowserRouter> tag only once to enclose the whole of your application.
Use this tag at the point of Rendering.
For Example
If you are using Create-React-app .Use this at your Index.js file
ReactDom.Render(<BrowserRouter><App/></BrowserRouter>)
Second Update:
Change your render code in react/index.js to
ReactDom.render(
<Router history={browserHistory} >
<Route path="/" component={Layout} >
<IndexRoute component={Index} ></IndexRoute>
<Route path="/exercise-1" name="exercise-1" component={Exercise1} ></Route>
<Route path="/exercise-2" name="exercise-2" component={Exercise2} ></Route>
<Route path="/exercise-3" name="exercise-3" component={Exercise3} ></Route>
<Route path="/exercise-4" name="exercise-4" component={Exercise4} ></Route>
<Route path="/exercise-5" name="exercise-5" component={Exercise5} ></Route>
<Route path="/exercise-6" name="exercise-6" component={Exercise6} ></Route>
<Route path="/:lessonName" name="lesson" component={Lesson} ></Route>
<Route path=":lessonName" name="lesson" component={Lesson} ></Route>
</Route>
</Router>
,app);
i..e the path starts with /

Resources