I have a phonebook app in which I'm implementing a search bar.
I am trying to use the useNavigate hook to take a string {term} and navigate to the search page along with the term the user typed in.
So for example if the user typed in David the address bar would be
http://localhost:3002/search?q=David
At the moment when I type David into search bar I get this result in address bar
http://localhost:3002/search
The parameter was not passed along to the search page.
I followed the instruction here which said I should do following
navigate('search', {term}) but this did not work for me
https://reactnavigation.org/docs/params/
Any help appreciated
Searchbar.js
import "./Searchbar.css"
import { useState } from "react"
import { useNavigate } from "react-router-dom"
export default function Searchbar() {
const [term, setTerm] = useState('')
const navigate = useNavigate()
const handleSubmit = function(e) {
e.preventDefault()
navigate('search', {term})
console.log(term)
}
return (
<div className="searchbar">
<form onSubmit={handleSubmit}>
<label htmlFor="search">Search Contacts</label>
<input
type="text"
onChange={(e) => setTerm(e.target.value)}
value={term}>
</input>
</form>
</div>
)
}
Navbar.js
import { Link } from "react-router-dom";
import "./Navbar.css"
import Searchbar from "./Searchbar";
export default function Navbar() {
return (
<div className="navbar">
<nav>
<Link to="/" className="brand"><h1>Phonebook</h1></Link>
<Searchbar></Searchbar>
<Link to="/create">Create Contact</Link>
</nav>
</div>
)
}
Some thing like this should work.
navigate({
pathname: "search",
search: {
q: term,
},
});
For more you can have a look here
Related
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.
I am changing the value in PC component but it is not reflected in the BR1 component. If I don't use react-router-dom, everything works fine, but I need the routes.
App.js code
import React, { createContext, useState } from 'react';
import './App.css';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import BR1 from './Components/BR1';
import PC from './Components/P_c1'
import BR from './Components/BR';
export const BRcontext = createContext();
function App() {
const [value, setValue] = useState(false)
return (
<div>
<BRcontext.Provider value={{value, setValue}}>
<Router>
<Routes>
<Route path='/PC' element={<PC/>} />
<Route path='/BR1' element={<BR1/>} />
<Route path='/BR' element={<BR/>} />
</Routes>
</Router>
</BRcontext.Provider>
</div>
);
}
export default App;
PC code
import React, { useContext } from 'react'
import './Profile.css';
import { BRcontext } from '../App';
export default function Profile() {
const {value, setValue} = useContext(BRcontext);
return (
<div>
<div className='container mt-5'>
<div className='row'>
<div>
<h3 className='mt-5'>Send Request</h3>
<button className='btn btn-success mt-3 ps-3 pe-3' onClick={()=>{setValue(true)}}>Request</button>
</div>
</div>
</div>
</div>
)
}
BR1 code
import React, { useContext } from 'react'
import BR from './BR'
import { BRcontext } from '../App'
import { Link } from 'react-router-dom';
export default function BR1() {
const {value} = useContext(BRcontext);
// let navigate = useNavigate();
return (
<div>
{console.log(value)} //this remains false
{value ? <Link to="/BR"/>: console.log('hello there!')}
</div>
)
}
In BR1 code, I want the value to become true when a button in the PC component is clicked
Link - https://codesandbox.io/s/great-star-bzhuvw?file=/src/App.js
It seems there's no way to navigate from /PC to /BR1 unless changing the browser URL directly, and by doing this, you lose the current context value because it's in memory. If you intend to keep this behaviour, you should consider persisting the context value every time you change it and initialize it with the previously persisted one.
An example using the browser's local storage:
// Helper function to read the storaged value if it exists
function getPersistedValue() {
const serializedValue = localStorage.getItem('value')
try {
if (!serializedValue) {
throw new Error('No previously persisted value found')
}
return JSON.parse(serializedValue)
} catch {
return false
}
}
// Using the helper function to initialize the state
const [value, setValue] = useState(getPersistedValue())
// Synchronizing the persisted value on local storage with the in-memory one
useEffect(() => {
localStorage.setItem('value', JSON.stringify(value))
}, [value])
If you want, I forked your Code Sandbox and applied these changes: https://codesandbox.io/s/router-context-forked-uqhzye.
I've created Keycloak theme with react using keycloakify package. I've edited the Terms file and when I'm clicking on accept it returns 404 page not found. This is my Terms component:
import { memo, useState } from "react";
import type { KcProps } from "keycloakify";
import type { KcContext } from "../kcContext";
import styles from "./Terms.module.scss";
import PDF from "./terms.pdf";
type KcContext_Terms = Extract<KcContext, { pageId: "terms.ftl" }>;
export const Terms = memo(
({ kcContext, ...props }: { kcContext: KcContext_Terms } & KcProps) => {
const [isDisabled, setIsDisabled] = useState(true);
const { url } = kcContext;
return (
<div className={styles.container}>
<div className={styles.terms_container}>
<p>
In order to continue, please carefully read and agree to our{" "}
<a href={PDF} target="_blank" onClick={() => setIsDisabled(false)}>
Terms of Service
</a>
</p>
<form
action={url.loginAction} method="POST">
<input
name="accept" id="kc-accept"type="submit" value="Accept" disabled={isDisabled}/>
</form>
</div>
</div>
);
}
);
The accept input has the same properties like the terms.ftl file in keycloack repo. can anyone tell me if I'm missing something?
There is a new way of implementing Terms and conditions with Keycloakify: https://docs.keycloakify.dev/terms-and-conditions
I want to redirect the page if the length of props.fields is zero. But the problem it is getting rendered twice so when the it is empty it gets redirected and doesn't wait for the second time.
I am new to React, it would be great if someone could help me.I have spent hours on fixing this.
import React, { Component, useEffect, useState } from 'react'
import Field from './field'
import {Redirect} from 'react-router-dom'
export default function FieldList(props) {
const [redirect,setRedirect] = useState(false);
useEffect(()=>{
if(props.fields.length===0) {
setRedirect(true)
}
else
setRedirect(false)
},[props.fields])
return (
<div>
<h1 style={{textAlign:"center", margin:"20px 0"}}>{props.text}</h1>
<div className='field-list'>
{props.fields.map((name) => (
<Field name={name} />
))}
</div>
{redirect && <Redirect to={'/'} />}
</div>
)
}
you can instead of Redirect either use a custom browser history or use react-router-dom's useHistory hook
to use useHistory hook
import {useHistory} from 'react-router-dom'
and then at theuseEffect
useEffect(()=>{
if(props.fields.length===0) {
useHistory().push('/')
}
},[props?.fields]
)
if you don't want to use that, here is what you can do to create a custom browser history
first create a file and call it History.js
add this line of code to it
import { createBrowserHistory } from "history";
export default createBrowserHistory();
and then import it to the file
import history from "FILE_PATH"
useEffect(()=>{
if(props.fields.length===0) {
history.push('/')
}
}
)
You should change your jsx to
<div>
<h1 style={{textAlign:"center", margin:"20px 0"}}>{props.text}</h1>
<div className='field-list'>
{props.fields.map((name) => (
<Field name={name} />
))}
</div>
{redirect ? <Redirect to={'/'} /> : null}
</div>
with RTL how do i check the to='/login' in Link to be true since RTL library mainly grabs by testid or text.
current page testing
import React from 'react';
import Proptypes from 'prop-types';
import { Link } from 'react-router-dom';
function Navigation(props) {
return (
<nav className="header__nav">
<Link to="/login">
<button type="button" className="header__login">LOGIN</button>
</Link>
</nav>
);
}
test
describe('Navigation Content', () => {
test('clicking login button', () => {
const props = jest.fn();
const { getByTestId, getByText } = render (
<Navigation popUpHandler={props}/>, { wrapper: MemoryRouter }
);
expect((<Link>).toHaveAttribute('to', '/login')
})
})
page i am trying to render to when clicked
function LoginForm() {
return (
<div className="login">
<h1 className="entryheader__header">Login</h1>
</div>
)
export default LoginForm;
Could you try rendering your component like this:
import { MemoryRouter } from 'react-router-dom'
...
...
const { getByTestId, getByText } = render(<YourComponent {...props}/>, { wrapper: MemoryRouter });
fireEvent.click(getByText('LOGIN'));
expect(getByText('some-text-on-login-page')).toBeInTheDocument();
This will wrap your component with the memory router rather than you doing it manually.
You can also try using BrowserRouter instead of MemoryRouter in your test file..
I wish I had an answer to this question but I do feel like I can say the following:
I wouldn't do
<Link to="/login">
<button type="button" className="header__login">LOGIN</button>
</Link>
because this will render non-valid HTML markup. It breaks accessibility rules to start