I just implemented a global search in my website and I started having issues with React-Router. It is not updating the view if the url changes parameters.
For example, navigating from /users/454545
to /teams/555555
works as expected. However, navigating from /teams/111111
to teams/222222
changes the url but the component is still /teams/111111
.
Here is my code fo the Search Input field.
const SearchResult = ({ id, url, selectResult, text, type }) => (
<Row key={id} onClick={() => selectResult(url)} width='100%' padding='5px 15px 5px 15px' style={{cursor: 'pointer'}}>
<Column alignItems='flex-start' style={{width: '100%'}}>
<Label textAlign='left' color='#ffffff'>{text}</Label>
</Column>
<Column style={{width: '100%'}}>
<Label textAlign='right' color='#ffffff'>{type}</Label>
</Column>
</Row>
)
const SearchInput = (props) => {
const { isSearching, name, onChange, onClear, results } = props;
return (
<Section width='100%' style={{display: 'flex', position: 'relative'}}>
<Wrapper height={props.height} margin={props.margin}>
<i className="fas fa-search" style={{color: 'white'}} />
<input id='search_input' placeholder={'Search for a team, circuit, or user'} name={name} onChange={onChange} style={{outline: 'none', backgroundColor: 'transparent', borderColor: 'transparent', color: '#ffffff', width: '100%'}} />
{onClear && !isSearching && <i onClick={onClear} className="fas fa-times-circle" style={{color: '#50E3C2'}} />}
{isSearching &&
<Spinner viewBox="0 0 50 50" style={{marginBottom: '0px', height: '50px', width: '50px'}}>
<circle
className="path"
cx="25"
cy="25"
r="10"
fill="none"
strokeWidth="4"
/>
</Spinner>
}
</Wrapper>
{results && <Section backgroundColor='#00121A' border='1px solid #004464' style={{maxHeight: '400px', position: 'absolute', top: '100%', left: '0px', width: '97%', overflowY: 'scroll'}}>
<Section backgroundColor='#00121A' style={{display: 'flex', flexDirection: 'column', padding: '15px 0px 0px 0px', justifyContent: 'center', alignItems: 'center', width: '100%'}}>
{results.length === 0 && <Text padding='0px 0px 15px 0px' color='#ffffff' fontSize='16px'>We didn't find anything...</Text>}
{results.length !== 0 && results.map(r => <SearchResult selectResult={props.selectResult} id={r._id} url={r.url} text={r.text} type={r.type} />)}
</Section>
</Section>}
</Section>
)
}
export default SearchInput;
The parent component is a nav bar which looks something like this. I've slimmed it down for readability.
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import SearchInput from '../shared/inputs/SearchInput';
const TopNav = (props) => {
const [search, setSearch] = useState(null);
const [searchResults, setSearchResults] = useState(null);
const debouncedSearchTerm = useDebounce(search, 300);
const [isSearching, setIsSearching] = useState(false);
function clearSearch() {
document.getElementById('search_input').value = '';
setSearchResults(null);
}
function searchChange(e) {
if (!e.target.value) return setSearchResults(null);
setSearch(e.target.value);
setIsSearching(true);
}
async function updateQuery(query) {
const data = {
search: query
}
const results = await api.search.query(data);
setSearchResults(results);
setIsSearching(false);
}
function selectResult(url) {
props.history.push(url);
setSearchResults(null);
}
function useDebounce(value, delay) {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
}
useEffect(() => {
if (debouncedSearchTerm) {
updateQuery(debouncedSearchTerm);
} else {
setSearchResults(null);
}
}, [user, debouncedSearchTerm])
return (
<ContentContainer style={{boxShadow: '0 0px 0px 0 #000000', position: 'fixed', zIndex: 1000}} backgroundColor='#00121A' borderRadius='0px' width='100%'>
<Section style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50px'}} width='1200px'>
<SearchInput height={'30px'} margin='0px 20px 0px 0px' isSearching={isSearching} selectResult={selectResult} onChange={searchChange} onClear={clearSearch} results={searchResults} />
</Section>
</ContentContainer>
)
}
function mapStateToProps(state) {
return {
user: state.user.data,
notifs: state.notifs
}
}
export default connect(mapStateToProps, { logout, fetchNotifs, updateNotifs })(TopNav);
Tl;DR
Using react-router for site navigation. Doesn't update component if navigating from /teams/111111
to /teams/222222
but does update if navigating from /users/111111
to /teams/222222
.
Any and all help appreciated!