Iterate through ReactJS components onClick - javascript

The goal is have this functionally work as a slider/slideshow.
Example Modal Components:
import React, { Component } from 'react';
const Modal_1 = () => {
return (
<li id="intro-res-slide" class="active">
<div>
<h2>Hi Im Mariah</h2>
</div>
</li>
)
}
export default Modal_1;
Presentational Component:
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Link,
Route,
Switch,
} from 'react-router-dom';
// Modals
import Modal_1 from "./modals/Modal_1.js"
import Modal_2 from "./modals/Modal_2.js"
import Modal_3 from "./modals/Modal_3.js"
import Modal_4 from "./modals/Modal_4.js"
import Modal_5 from "./modals/Modal_5.js"
const _modals = [ Modal_1, Modal_2, Modal_3, Modal_4, Modal_5 ]
const HelperModalRender = (props) => (
<div class="tool-slides active slideshow">
<ul>
{/* ITERATE THROUGH MODALS HERE */}
{ _modals[props.currentSlide] }
</ul>
<div
onClick={props.prevModal}
className="btn dec"
></div>
<div
className="btn inc"
onClick={props.nxtModal}
></div>
</div>
)
export default HelperModalRender;
Container Component:
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Link,
Route,
Switch,
} from 'react-router-dom';
import HelperModalRender from './HelperModalRender.js'
class HelperModalContainer extends Component {
constructor() {
super();
this.state = {
currentSlide: 1,
slideActive: true
}
this.prevModal = this.prevModal.bind(this)
this.nxtModal = this.nxtModal.bind(this)
}
prevModal(){
var currentSlide = this.state.currentSlide
this.setState({ currentSlide: currentSlide++ })
}
nxtModal(){
var currentSlide = this.state.currentSlide
this.setState({ currentSlide: currentSlide-- })
}
render() {
return (
<HelperModalRender
active = {this.state.slideActive}
currentSlide = {this.state.currentSlide}
prevModal = {this.state.prevModal}
nxtModal = {this.state.nxtModal}
/>
)
}
}
export default HelperModalContainer;
I was hoping to have a function in my container component that would iterate through the _modals array, returning the corresponding component - but a switch statement is not an option and I'm having trouble thinking of an alternative.

This should work, but it looks like your container component's render method has a typo. Try changing HelperModalRender's currentSlide prop to:
currentSlide = {this.state.currentSlide}
You may also need to start the slides at zero instead of 1, to match the array index of the slide components.

realized the issue lied in this warning:
This may happen if you return a Component instead of <Component /> from render.
so I updated the _modals array to:
const _modals = [ <Modal_1 />, <Modal_2 />, <Modal_3 />, <Modal_4 />, <Modal_5 /> ]

Related

How to pass data between pages in React with react-router-dom v6?

I am trying to open a new page from the home page where the data is passed on clicking the item on the list. The code I have works for older versions of react-router-dom I believe. Can someone tell me how can I do the same thing in newer version of React? I need to display the ID and title in the new page.
I tried to follow the example here https://codesandbox.io/s/focused-wright-w8il9?file=/src/ViewUserDetails.js:354-377
The error says AppPage.js:15 Uncaught TypeError: Cannot read properties of null (reading 'DUMMY_DATA')
If there are any corrections otherwise too, please correct me. I'm new to this.
Thank You.
GridList.js
import React from "react";
import ReactDOM from "react-dom";
import { Link, useNavigate } from "react-router-dom";
import { Component } from "react";
import GridItem from "./GridItem";
import AppPage from "./AppPage";
import classes from "./GridList.module.css";
import App01 from "./app-01.png";
import App02 from "./app-02.png";
const GridList = (props) => {
const DUMMY_DATA = [
{
title: "tic tac toe",
id: 1,
image: App01,
},
{
title: "snake",
id: 2,
image: App02,
},
];
return (
<div className={classes.gridc1}>
{DUMMY_DATA.map((item) => {
return (
<div key={item.id}>
<Link
to={{
pathname: `/apppage/${item.id}`,
state: { DUMMY_DATA: item },
}}
>
<GridItem key={item.id} image={item.image} />
</Link>
</div>
);
})}
</div>
);
};
export default GridList;
AppPage.js
import React from "react";
import {ReactDOM, render } from "react-dom";
import { useLocation } from "react-router-dom";
import { Component } from "react";
const AppPage = _ => {
const { state }= useLocation();
return (
<div>
<p>{state.DUMMY_DATA.id}</p>
<p>{state.DUMMY_DATA.title}</p>
</div>
);
};
export default AppPage;
I have solved this by following a documentation for upgrading to v6 react-router-dom and it finalllllly worked. Here it is: https://reactrouter.com/docs/en/v6/getting-started/tutorial
The solution was simple. I was just looking for a better documentation for upgrading from v5 to v6, I guess.
Also, I will attach the code if it's gonna help anybody here. (I have renamed a few things like 'DUMMY_DATA' to 'items' just for my convenience. The code would explain it clearly anyway ). If there are any doubts further, do comment and I'll try to reply! :)
GridList.js
import React from "react";
import ReactDOM from "react-dom";
import { Link, useParams } from "react-router-dom";
import { Component } from "react";
import GridItem from "./GridItem";
import AppPage from "./AppPage";
import classes from "./GridList.module.css";
import { getItems } from "./Data";
let items = getItems();
const GridList = (props) => {
return (
<div className={classes.gridc1}>
{items.map((item) => {
return (
<div key={item.id}>
<Link to={`/apppage/${item.id}`} key={item.id}>
<GridItem key={item.id} image={item.image} />
</Link>
</div>
);
})}
</div>
);
};
export default GridList;
Data.js
import React from "react";
import ReactDOM, { render } from "react-dom";
import App01 from "./app-01.png";
import App02 from "./app-02.png";
let items = [
{
title: "tic tac toe",
id: 1,
image: App01,
},
{
title: "bytes",
id: 2,
image: App02,
},
];
export function getItems() {
return items;
}
export function getItem(id) {
return items.find((item) => item.id === id);
}
AppPage.js
import React from "react";
import ReactDOM, { render } from "react-dom";
import { useParams } from "react-router-dom";
import { Component } from "react";
import { getItem } from "./Data";
const AppPage = _ => {
let params = useParams();
let item = getItem(parseInt(params.id, 10));
return (
<div>
<p ptitle = {item.title} />
<p pid = {item.id}> />
</div>
);
};
export default AppPage;
import React from "react";
import ReactDOM, { render } from "react-dom";
import Body from "../ui/Body";
import Head from "../ui/Head";
import Tail from "../ui/Tail";
import { useLocation } from "react-router-dom";
import { Component } from "react";
const AppPage = () => {
//don't forget to use this, good luck :)
const { state } = useLocation();
return (
<div>
// and use "state", don't use static
// what is static ?
<p>{state.DUMMY_DATA.id}</p>
<p>{state.DUMMY_DATA.title}</p>
</div>
);
};
export default AppPage;

How to use react-router-dom v6 navigate in class component

I installed react-router-dom v6 and I want to use a class based component, in previous version of react-router-dom v5 this.props.history() worked for redirect page after doing something but this code not working for v6 .
In react-router-dom v6 there is a hook useNavigate for functional component but I need to use it in class base component , Please help me how to use navigate in class component ?
In the react-router-dom v6, the support for history has been deprecated but instead of it, navigate has been introduced. If you want to redirect user to a specific page on success of a specific event, then follow the steps given below:
Create a file named as withRouter.js, and paste the code given below in this file:
import { useNavigate } from 'react-router-dom';
export const withRouter = (Component) => {
const Wrapper = (props) => {
const navigate = useNavigate();
return (
<Component
navigate={navigate}
{...props}
/>
);
};
return Wrapper;
};
Now, in whichever class based component you want to redirect the user to a specific path/component, import the above withRouter.js file there and use this.props.navigate('/your_path_here') function for the redirection.
For your help, a sample code showing the same has been given below:
import React from 'react';
import {withRouter} from '.your_Path_To_Withrouter_Here/withRouter';
class Your_Component_Name_Here extends React.Component{
constructor(){
super()
this.yourFunctionHere=this.yourFunctionHere.bind(this);
}
yourFunctionHere()
{
this.props.navigate('/your_path_here')
}
render()
{
return(
<div>
Your Component Code Here
</div>
)
}
}
export default withRouter(Your_Component_Name_Here);
Above Code works Perfect. And this is just a small extension.
If you want onclick function here is the code:
<div className = "row">
<button className= "btn btn-primary"
onClick={this.yourFunctionHere}>RedirectTo</button>
</div>
in class base component for redirect user follow this step :
first import some component like this
import { Navigate } from "react-router-dom"
now make a state for Return a boolean value like this:
state = {
redirect:false
}
now insert Naviagate component to bottom of your component tree
but use && for conditional rendring like this :
{
this.state.redirect && <Navigate to='/some_route' replace={true}/>
}
now when you want redirect user to some page just make true redirect state
on a line of code you want
now you can see you navigate to some page :)
Try this:
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
export const withRouter = (Component) => {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
and just used this function, in my case:
import { withRouter } from '../utils/with-router';
import './menu-item.styles.scss';
const MenuItem = ({title, imageUrl, size, linkUrl,router}) =>(
<div
className={`${size} menu-item`} onClick={() => router.navigate(`${router.location.pathname}${linkUrl}`)}
>
<div className='background-image'
style={{
backgroundImage: `url(${imageUrl})`
}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
)
export default withRouter(MenuItem);
I found this solution here https://www.reactfix.com/2022/02/fixed-how-can-i-use-withrouter-in-react.html
Other solution is useNavigate, for example:
<button onClick={() => {navigate("/dashboard");}} >
Dashboard
</button>
In a react class component use <Navigate>. From the react router docs:
A <Navigate> element changes the current location when it is rendered. It's a component wrapper around useNavigate, and accepts all the same arguments as props.
Try creating a reusable functional Component like a simple button and you can use it in your class component.
import React from "react";
import { useNavigate } from "react-router-dom";
const NavigateButton = ( { buttonTitle, route,isReplaced}) => {
const navigate = useNavigate();
return (
<button
className = "btn btn-primary"
onClick = { () => {
navigate( route , {replace:isReplaced} )
}}
>
{buttonTitle}
</button>;
);
});
export default NavigateButton;
After this, you can use NavigateButton in any of your class Components. And it will work.
<NavigateButton title = {"Route To"} route = {"/your_route/"} isReplaced = {false}/>
Found this explanation from the GitHub react-router issue thread, this explained how to use react-router 6 with class components
https://github.com/remix-run/react-router/issues/8146
I got this code from the above issue explanation
import React,{ Component} from "react";
import { useNavigate } from "react-router-dom";
export const withNavigation = (Component : Component) => {
return props => <Component {...props} navigate={useNavigate()} />;
}
//classComponent
class LoginPage extends React.Component{
submitHandler =(e) =>{
//successful login
this.props.navigate('/dashboard');
}
}
export default withNavigation(LoginPage);
If you need to use params for data fetching, writing a logic in your ClassComponent and render component depending on them, then create wrapper for your ClassComponentContainer
import { useLocation, useParams } from 'react-router-dom';
import ClassComponentContainer from './ClassComponentContainer';
export default function ClassComponentWrap(props) {
const location = useLocation();
const params = useParams();
return <ClassComponentContainer location={location} params={params} />
}
after it just use params in ClassComponent which is in props
import React from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import PresentationComponent from './PresentationComponent';
class ClassComponent extends React.Component {
componentDidMount() {
let postID = this.props.params.postID;
axios.get(`https://jsonplaceholder.typicode.com/posts/${postID}`)
.then((response) => {console.log(response)})
}
render() {
return <PresentationComponent {...this.props} />
}
}
const mapStateToProps = (state) => {...}
const mapDispatchToProps = (dispatch) => {...}
const ClassComponentContainer = connect(mapStateToProps, mapDispatchToProps)(ClassComponent);
export default ClassComponentContainer;
and use ClassComponentWrap component in Route element attribute
import { BrowserRouter, Route, Routes } from "react-router-dom";
import ClassComponentWrap from './components/ClassComponentWrap';
export default function App(props) {
return (
<BrowserRouter>
<Routes>
<Route path="/posts/:postID?" element={<ClassComponentWrap />} />
</Routes>
</BrowserRouter>
);
}
Here is my solution:
import React, { Component } from "react";
import { useNavigate } from "react-router-dom";
class OrdersView extends Component {
Test(props){
const navigate = useNavigate();
return(<div onClick={()=>{navigate('/')}}>test{props.test}</div>);
}
render() {
return (<div className="">
<this.Test test={'click me'}></this.Test>
</div>);
}
}

Want to use switcher function in class component of react js?

I am not able to use this switcher method in my react app. which is build using class components
Suggest me way to use this switcher method of useThemeSwitcher() function in class components.
How can i Use or (write )this function in my web app.???
In this method switcher function is used in functional component..
import React from "react";
import "./App.css";
import { useThemeSwitcher } from "react-css-theme-switcher";
import { Switch } from "antd";
function App() {
const [isDarkMode, setIsDarkMode] = React.useState("false");
const { switcher, themes } = useThemeSwitcher();
const toggleTheme = (isChecked) => {
setIsDarkMode(isChecked);
switcher({ theme: isChecked ? themes.dark : themes.light })
};
return (
<div className="main fade-in">
<Switch checked={isDarkMode} onChange={toggleTheme} />
</div>
);
}
export default App;
I want use switcher function in this code..
import React from "react";
import { Layout, Button, Menu, Popconfirm, Dropdown, Select } from 'antd';
import { useThemeSwitcher } from "react-css-theme-switcher";
import { Switch, Input } from "antd";
class Header extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = this.toggleTheme.bind(this);
this.state = {
isDarkMode:false,
};
}
toggleTheme = (isChecked)=>
{
this.setState({isDarkMode:isChecked ? true : false})
}
render() {
return (
<div className="main fade-in">
<Switch checked={this.state.isDarkMode} onChange={this.toggleTheme} />
</div>
)
}
}
export default Header;
Since it's a hook you'll need to use it in a functional component. Simply translate your class component to a functional one.
const Header = () => {
const [isDarkMode, setIsDarkMode] = useState(false);
// Now we can use it
const { switcher, themes } = useThemeSwitcher();
return <div className="main fade-in">
<Switch checked={this.state.isDarkMode} onChange={setIsDarkMode} />
</div>;
};

my react code is working but when i refresh the page i get TypeError: Cannot read property 'Location' of undefined

Starting with GamePage, it provides 2 routes which renders the components GameList and GameDetailPage. Both work fine at first but When i refresh the page for Gamelist component, it still rerenders the page but when i refresh the page for GameDetailPage, i get the error TypeError: Cannot read property 'Location' of undefined. I do not understand why it is unable to fetch data from state whenever i refresh.
gamepage.jsx
import React from "react";
import GamesList from "../../components/games-list/game-list.component";
import { Route } from "react-router-dom";
import GameDetailPage from "../gamedetailpage/gamedetailpage.component";
import {firestore,convertCollectionsSnapshotToMap} from '../../firebase/firebase.utils'
import {connect} from 'react-redux'
import {updateFootballGames} from '../../redux/games/games.actions'
class GamePage extends React.Component {
unsubscribeFromSnapshot=null;
//whenever the component mounts the state will be updated with the football games.
componentDidMount(){
const {updateFootballGames}=this.props
const gameRef=firestore.collection('footballgames')
gameRef.onSnapshot(async snapshot=>{
const collectionsMap=convertCollectionsSnapshotToMap(snapshot)
updateFootballGames(collectionsMap)
})
}
render() {
const { match } = this.props;
return (
<div className="game-page">
<h1>games page</h1>
<Route exact path={`${match.path}`} component={GamesList} />
<Route path={`${match.path}/:linkUrl`} component={GameDetailPage}
/>
</div>
);
}
}
const mapStateToProps=state=>({
games:state.games.games
})
const mapDispatchToProps=dispatch=>({
updateFootballGames:collectionsMap=>
dispatch(updateFootballGames(collectionsMap))
})
export default connect(mapStateToProps, mapDispatchToProps)(GamePage);
gamedetailpage.component.jsx
import React from "react";
import { connect } from "react-redux";
import GamePreview from '../../components/game-preview/game-preview.component'
import GameDetails from '../../components/game-details/game-details.component'
const GameDetailPage = (props) => {
const {games, match} = props
const urlparam =match.params.linkUrl
// const games_array = Object.entries(games)
const gameObj=games[urlparam]
console.log('prop',gameObj)
return (
<div className="game-list">
<GameDetails game = {gameObj}/>
</div>
);
};
const mapStateToProps = (state) => ({
games: state.games.games,
});
export default connect(mapStateToProps)(GameDetailPage);
game_details.component.jsx
import React from 'react';
const GameDetails = (props) => {
console.log(props.game.Location)
return(
<div>
Location:{props.game.Location}
<br/>
Price:{props.game.Price}
</div>
)
}
export default GameDetails;
gamelist.component.jsx
import React from "react";
import './game-list.styles.scss'
import GamePreview from "../game-preview/game-preview.component";
import {connect} from 'react-redux'
const GameList=(props)=>{
const {games}=props
console.log(games)
const game_list=Object.entries(games)
console.log(game_list)
return (
<div className="game-list">
{game_list.map(game =>
<GamePreview game = {game[1]}/>)}
</div>
);
}
const mapStateToProps=state=>({
games:state.games.games
})
export default connect(mapStateToProps)(GameList);
gamepreview.component.jsx
import React from "react";
import "./game-preview.styles.scss";
import { withRouter, Route } from "react-router-dom";
import GamePreviewDetail from "../game-preview-detail/game-preview-detail.component";
const GamePreview = (props) => {
const { Location, Time, linkUrl, Price } = props.game;
const { history, match } = props;
return (
<div
className="game-preview"
onClick={() => history.push(`${match.url}/${linkUrl}`)}
>
<div className="game-preview-image">
<p>Picture goes here</p>
</div>
{/* <GamePreviewDetail name = {Location} price={Price}/> */}
<p>Location:{Location}</p>
<p>Price:{Price}</p>
</div>
);
};
export default withRouter(GamePreview);
app.js
import React from 'react';
import './App.css';
//import dependencies
import { Route, Switch } from "react-router-dom";
//import pages
import HomePage from './pages/homepage/homepage'
import GamesPage from './pages/gamespage/gamespage'
import SignInSignUp from './pages/signin-signup-page/signin-signup-page'
import GameDetailPage from "./pages/gamedetailpage/gamedetailpage.component";
import Header from './components/header/header.component';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
class App extends React.Component{
constructor() {
super();
this.state = {
currentUser: null
}
}
unsubscribeFromAuth = null
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
// check if the snapshot has changed (subscribe)
// get the user that we just created or that already exists in the db
userRef.onSnapshot(snapshot => {
this.setState({
currentUser: {
id: snapshot.id,
...snapshot.data()}
})
})
} else {
this.setState({currentUser: userAuth})
}
})
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render(){
return(
<div>
<Header currentUser = {this.state.currentUser}/>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/games" component={GamesPage} />
<Route exact path="/signin" component={SignInSignUp} />
</Switch>
</div>
)
}
}
export default App;
I would try using useParams hook instead. Then capturing any changes of linkUrl with useEffect hook. Also introducing gameObj with useState.
useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.
If you're familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
Try to modify <GameDetailPage /> component as the following:
import React, { useState, useEffect } from 'react';
import { useParams } from "react-router-dom";
// other imports
const GameDetailPage = (props) => {
const { games } = props;
let { linkUrl } = useParams();
const [ gameObj, setGameObj ] = useState(null);
useEffect(() => {
if (games) {
const newGameObj = games[linkUrl];
console.log('game object', newGameObj);
setGameObj(newGameObj);
}
}, [games, linkUrl]);
return <div className="game-list">
{ gameObj && <GameDetails game={ gameObj } /> }
</div>
}
+1 - null check:
Also you can see a null check in the return statement for gameObj which helps rendering only that case once you have a value in games array with found linkUrl value.
I hope this helps!

React.js test fails on finding components

I have an app build on React, Redux and React-router. I'm writing test using React TestUtils and I found that from the tests you can see below.
The first expect works: expect(nav).to.have.length(1);
but the second one expect(modal).to.have.length(1);
fails with:
AssertionError: expected [] to have a length of 1 but got 0
App.js:
import React, { Component, cloneElement, PropTypes } from 'react';
import ContactsList from './contactsList';
import Nav from './nav';
import Modal from './modal';
import Header from './header';
import HomeIndex from './homeIndex';
import ErrorBox from './errorBox';
import ImmutablePropTypes from 'react-immutable-proptypes';
export default class App extends Component {
render = () => {
const { actions, contacts, router } = this.props;
return (
<div>
<Nav />
<div className="container">
<ErrorBox error={contacts.getIn(['error', 'errorMessage'])} show={contacts.getIn(['error', 'showError'])} />
<Header />
<div className="contacts-list-container">
<ContactsList contacts={contacts} />
<Modal open={contacts.get('showSpinner')} />
{ cloneElement(this.props.children || <HomeIndex/>, { contacts: contacts ,
addContact: actions.addContactReq,
getContact: actions.getContact,
contact: contacts.get('contact'),
router: router,
deleteContact: actions.deleteContact,
editContact: actions.editContact }) }
</div>
</div>
</div>
);
}
}
App.propTypes = {
actions: PropTypes.object.isRequired,
contacts: ImmutablePropTypes.map.isRequired,
router: PropTypes.object.isRequired
};
App-spec.js:
import React from 'react';
import { renderIntoDocument, scryRenderedDOMComponentsWithTag } from 'react-addons-test-utils';
import { expect } from 'chai';
import App from '../components/app';
import { Map } from 'immutable';
describe('app', () => {
it('renders properly', () => {
const component = renderIntoDocument(
<App actions={{}} router={{}} contacts={ Map({
showSpinner: false,
error: Map({
showError: false,
errorMessage: ''
}),
contacts: Map(),
contact: Map()
}) } />
);
const nav = scryRenderedDOMComponentsWithTag(component, 'Nav');
const modal = scryRenderedDOMComponentsWithTag(component, 'Modal');
expect(nav).to.have.length(1);
expect(modal).to.have.length(1);
});
});
scryRenderedDOMComponentsWithTag looks for actual HTML elements in the component DOM. You want to use scryRenderedComponentsWithType to find rendered React components:
const modal = scryRenderedComponentsWithType(component, Modal);
See https://facebook.github.io/react/docs/test-utils.html#scryrenderedcomponentswithtype
Your Nav call probably works because you have a <nav> HTML element in your Nav React component.
scrying needs a component reference iirc. So:
import Modal from './components/modal';
// Then later:
const modal = scryRenderedComponentsWithType(component, Modal);
This will look for instances of the actual component.

Categories