React hooks : 'Cannot read property 'push' of undefined' - reactjs

I'm trying to redirect my homepage to "/call" page based on a redux state. I can go to that component by typing the url manually but cant do it with a function. I tried "Redirect to", "history.push" but none of them worked for me. I cant solve the problem. Here is my code;
const Phone = ({ hidden, photoOpened, inCall }) => {
const dispatch = useDispatch(getContacts());
let history = useHistory();
useEffect(() => {
if (inCall.inCall) {
history.push('/call')
}
}, [inCall]);
useEffect(() => {
dispatch(getContacts());
}, [])
return (
<div hidden={process.env.NODE_ENV === 'development' ? !hidden : hidden} className={photoOpened ? "phone-container-rotate" : "phone-container"}>
<div className="coque" />
<Suspense fallback={<div className="animated fadeIn pt-1 text-center">Loading...</div>}>
<HashRouter basename="/phone">
<div
className="phone-content"
style={{ backgroundImage: `url(${background})` }}
>
<HeaderBar />
<BannerNotifications />
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
render={props => <route.component {...props} />}
/>
) : null;
})}
</Switch>
</div>
<Route component={BottomPhoneNavigator} />
</HashRouter>
</Suspense>
</div>
);
};

You could try and test for history existence of the history in your effect, also add it to dependency list
useEffect(() => {
if (history && inCall.inCall) {
history.push('/call')
}
}, [inCall, history]);
And important thing, your component using this hook must be within the Router, I see you'\re using HashRouter but as child of component using the hook.
Also if you're stuck to this structure, why wont you try to use Redirect within the Switch? This could work with some smart test so you wont end up in a loop:)

To use history your Phone component should be inside router component

Related

How to create a function that renders a component with map?

I'm trying to render a component that is actually created from a request from the backend, which brings me an array and so on.
However, I am getting the following error in the component:
const RouteMicrofrontend: () => false | (JSX.Element | undefined)[][]
'RouteMicrofrontend' cannot be used as a JSX component.
Its return type 'false | (Element | undefined)[][]' is not a valid JSX element.
Type 'boolean' is not assignable to type 'ReactElement<any, any>'.ts(2786)
Is it possible to create a function that renders a component from .map? How to do this?
const RouteMicrofrontend = useCallback(
() =>
microfrontendNav.length > 0 &&
microfrontendNav.map(({ ListMenu }) =>
ListMenu.map(
({ microfrontend }) =>
microfrontend && (
<Route key={microfrontend.registry} path={microfrontend.data.basename}>
<MicrofrontendContainer>
<Loader
service={microfrontend.service}
module={microfrontend.module}
registry={microfrontend.registry}
data={microfrontend.data}
fallback={<MicrofrontendFallback />}
suspense={<MicrofrontendSuspense />}
/>
</MicrofrontendContainer>
</Route>
)
)
),
[microfrontendNav]
);
return (
<BrowserRouter>
<MicrofrontendNav sectionList={microfrontendNav} />
<TickerCarousel />
<Header />
<Switch>
<Route exact path='/' component={() => <div />} />
<Route exact path='/404' component={() => <Error404 />} />
<RouteMicrofrontend />
<Route path='*' component={() => <Redirect to='/404' />} />
</Switch>
</BrowserRouter>
);
The provided compiler error pretty much explains the whole problem. Type false | (Element | undefined)[][] is not a valid React component.
You cannot return multiple elements from a component function (DOM diffing, etc.). Change your code that the RouteMicrofrontend function returns: null when the conditions are not met or a single element with map contents as children.
Aside - You cannot return false from a React component. If you want to render nothing from a React component, return null.
You can conditionally render something (such as an array) using the ternary operator.
Imagine you want to render an Array<Item>:
interface ItemModel {
}
function Item({ item } : { item: ItemModel }) {
return (<div>Render Something Here</div>);
}
function MicroFrontend({ items }: { items: Item[] }) {
return (
<>
...
{ items.length && <h1>Items</h1> }
{ items.length > 0 ? items.map(item => <Item item={item} />) : null }
...
</>
);
}

Combining 2 or more dynamic imports into one chunk for webpack

I am code splitting a react-webpack application based on which route is loaded in the browserRouter. Now I want to package 2 or more dynamic imports into one chunk. For example I have a route /A which renders the A Dashboard and another /A/{id} which shows the details view for a single item. Since, when my users navigate to the dashboard, there is a high possibility that they would also open the details view for some item, I want to package both the dynamically imported components in the same chunk. Is this possible?
let aDetails: any;
let aDashboard: any;
const [isLoadedADashboard, setIsLoadedADashboard] = useState<boolean>(false);
const [isLoadedADetails, setIsLoadedADetails] = useState<boolean>(false);
return (
<BrowserRouter>
<Route
exact
path="/A"
render={(props) => {
import('../A/Dashboard').then((mod) => {
aDashboard = mod.default;
setLoadedADashboard(true);
});
return loadedADashboard ? (
<aDashboard />
) : <div />;
}}
/>
<Route
exact
path="/A/{id}"
render={(props) => {
import('../A/Details').then((mod) => {
aDetails = mod.default;
setLoadedADetails(true);
});
return loadedADetails ? (
<aDetails />
) : <div />;
}}
/>
</BrowserRouter>
);
You can try using webpack magic comments inside the dynamic imports. For both the dynamic imports use the same webpackChunkName to ensure that both the files are bundled under same chunk.
import(/* webpackChunkName: 'dashboard' */'../A/Dashboard').then((mod) => {
aDashboard = mod.default;
setLoadedADashboard(true);
});
import(/* webpackChunkName: 'dashboard' */'../A/Details').then((mod) => {
aDetails = mod.default;
setLoadedADetails(true);
});
Note: we are using the same chunk name for both the imports.
Create proxy-module which includes both modules you need to be in one chunk:
import Dashboard from 'Dashboard';
import Details from 'Details';
export {Dashboard, Details};
Now let say the name of the module above is 'Proxy' (just for example) and it's located in the same directory as your modules. You can load it dynamically:
<Route
exact
path="/A"
render={(props) => {
import('../A/Proxy').then((mod) => {
aDashboard = mod.Dashboard;
setLoadedADashboard(true);
});
return loadedADashboard ? (
<aDashboard />
) : <div />;
}}
/>
<Route
exact
path="/A/{id}"
render={(props) => {
import('../A/Proxy').then((mod) => {
aDetails = mod.Details;
setLoadedADetails(true);
});
return loadedADetails ? (
<aDetails />
) : <div />;
}}
/>

I cannot render the data from my db in React.js project "TypeError: Cannot read property 'title' of undefined"

I'm using Axios to get data from my API but when I try to render the state out I get an error of : "TypeError: Cannot read property 'title' of undefined".
I used the same code for a previous component and it seemed to work.
Data looks like this:
[
{
"portfolio": {
"title": "Portfolio",
"subTitle": "Checkout my works"
},
"cv": {
"title": "Download my cv",
"subTitle": "Aviable in three languages"
},
"contact": {
"title": "Let's talk",
"subTitle": "Send me and e-mail"
},
"_id": "5eff2aefa386601f00c98b7d",
"__v": 0
}
]
code in App.js file :
I just used the state to pass data trough the portfolio component.
<Route path="/portfolio" render={() => (<Portfolio title={state.header.portfolio.title} subTitle={state.header.portfolio} />)} />
Entire code:
const App = () => {
const [state,setState] = useState({
header:[]
})
useEffect(() => {
axios.get('http://localhost:8080/api/metainfo')
.then(res=>res)
.then(data=>{
// console.log(data.data)
setState(prevState=>{
return{...prevState,header:data.data}
})
})
},[]);
return (
<Router>
<div className="App">
<NavBar />
<div className="app-wrap">
<Switch>
<Route path="/" exact render={Home} />
<Route path="/portfolio" render={() => (<Portfolio title={state.header.portfolio.title} subTitle={state.header.portfolio} />)} />
<Route path="/cv" component={CV} />
<Route path="/contact" component={Contact} />
<Route path="/footer" component={Footer} />
</Switch>
</div>
</div>
</Router>
);
}
Errors: Image with errors
Please help
First, instead of creating your own setState, I would change your state hook to be:
const [header, setHeader] = useState([]);
That is, assuming your data is an array. I'm not sure why it's an array, but that's what you've provided.
Next, your useEffect has an unnecessary .then() line in it. I would change it like so:
useEffect(() => {
axios.get('http://localhost:8080/api/metainfo')
.then(res=>{
// console.log(res.data)
setHeader(res.data)
// Use [...res.data] if necessary
})
},[]);
Finally, because your header state object is an array, you will need to reference it in your Route like so:
<Route path="/portfolio" render={() => (<Portfolio title={header.length > 0 ? header[0].portfolio.title : ''} subTitle={header.length > 0 ? header[0].portfolio.subTitle : ''} />)} />
I've edited my answer to include that from blankart.
It seems like your error comes from the initial load of your Component. In order to fix this, change your Portfolio props
<Portfolio title={state.header.length > 0 ? state.header[0].portfolio.title : ''} subTitle={state.header.length > 0 ? state.header[0].portfolio.subTitle : ''} />

Can't redirect to search result page

I'm not good at English, so it might be hard to explain my intention.
I'm using React, React router, Apollo client,
In production build, When I click the search button, I can't redirect to render a result component because of error with error message
error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
First, I tried without useEffect. It occur error as described above.
Second, I tried with useEffect hook, that change redirect state to false. and it changes url. but doesn't render result component.
useEffect(() => {setRedirect(false)}, [redirect])
Finally I tried to make another react app to test this situation without apollo, and some custom components that are for clean code in the production build. I tried in development build. And it perfectly works without error
// search-bar.js
function SearchBar(props) {
// keywords for searching books
const [keywords, setKeywords] = useState('');
// search option
const [option, setOption] = useState('All');
// determine to redirect or not
const [redirect, setRedirect] = useState(false);
return (
<>
<div className="nav-search">
<form
role="search"
onSubmit={ e => {
e.preventDefault();
setRedirect(true);
}}
className="form-search"
>
<NotRequiredInput
type="search"
label="Search keywords: "
id="keywords"
value={keywords}
onChange={e => {
setKeywords(e.target.value);
}}
/>
// it map the list's items to option
<SelectBoxWithoutNone
label="Search option: "
id="search-option"
value={option}
onChange={e => {
setOption(e.target.value);
}}
list={['All', 'Title', 'Author']}
/>
<SubmitButton label="Search" />
</form>
</div>
{ redirect && <Redirect to={`/search?o=${option}&k=${keywords}`} /> }
</>
);
}
// app.js
<Query>
{({loading, data}) => {
if (loading)
return (
<Header>
<NavBar>
<main>
<Suspense>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/search" render={props => <SearchResult {...props}/>}
)
}}
// app.js looks like this with many route component.
// also using React.lazy for code splitting
// search-result.js
function SearchResult (props) {
const parsed = queryString.parse(props.location.search);
const option = parsed.o;
const keywords = parsed.k;
return (
<div className="div-search-result">
<p>{option}</p>
<p>{keywords}</p>
</div>
);
}
I expected that it renders result component (with or without hooks)
but as I described above, It occurred error
Update: When I tried to type some query parameter on url path directly, it works.

React collect all properties as one props and put it into component or NOT?

I'm reading this following code from https://reacttraining.com/react-router/web/example/route-config
const RouteWithSubRoutes = route => (
<Route
path={route.path}
render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes} />
)}
/>
);
const RouteConfigExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
</div>
</Router>
);
Please look at the first line, why it's not this:
const RouteWithSubRoutes = ({route}) => (
As I know, this arrow function should get one parameter which we often call it as props, which should be a collection include all properties that be put in. In this case the props should include 'key' and all of properties of 'route'.
In the component of the arrow function, the RouteWithSubRouters, we should filter the useful properties from the collection props, such as route, so we write the parameters as ({route}).
Am I get it wrong? Why it show error when I change it to ({route})?
===================================================================
Thanks to all! Now I know the parameter magic. I change code as following:
const RouteWithSubRoutes = (routeProps) => {
console.log(routeProps.aaa)
return (
<Route
path={routeProps.path}
render={props => (
// pass the sub-routes down to keep nesting
<routeProps.component {...props} routes={routeProps.routes} />
)}
/>
);
const RouteConfigExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
{routes.map((route, i) => <RouteWithSubRoutes key={i} aaa="bbb" {...route} />)}
</div>
</Router>
);
I get the print 'bbb'~.
It's easier to understand if the parameter is named as 'props'.
In this code route is an object that contains values that have to be used to create <Route>. Since props name is ambiguous and is used within same component, it could be defined for explicitness as:
const RouteWithSubRoutes = routeProps => (
<Route
path={routeProps.path}
render={props => (
// pass the sub-routes down to keep nesting
<routeProps.component {...props} routes={routeProps.routes} />
)}
/>
);
Or with destructuring (component needs to be renamed to upper-case):
const RouteWithSubRoutes = ({ path, routes, component: Component }) => (
<Route
path={path}
render={props => (
<Component {...props} routes={routes} />
)}
/>
);
const RouteWithSubRoutes = ({route}) => (...) would be a mistake because routeProps object that RouteWithSubRoutes receives doesn't have route property.
In:
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
specifically in {...route}, you are not passing the component RouteWithSubRoutes a prop named route. By using the spread syntax (More here), you are passing every property of the routeobject as an individual prop. Thus, the first parameter of the render function does not actually contain a route object, it contains key and every one of the properties of route.
For example, if the route object looks like this:
{
something: "value",
somethingElse: "otherValue",
}
Then doing
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
is equivalent to doing
{routes.map((route, i) => <RouteWithSubRoutes
key={i}
something={route.something}
somethingElse={route.somethingElse}
/>)}
What you actually seem to want to do is to:
{routes.map((route, i) => <RouteWithSubRoutes key={i} route={route} />)}
Where you pass your route object as the prop named route, allowing you to do:
const RouteWithSubRoutes = ({route}) => (
//THE RENDER OF THE ROUTE HERE
)
When you say:
As I know, this arrow function should get one parameter which we often call it as props, which should be a collection include all properties that be put in. In this case the props should include 'key' and all of properties of 'route'.
you are understanding how the spread syntax works. Props should include keyand all of the properties of route, and route does not include a property named route unless your actual routeobject contains inside another object named route, like so:
{
...
route: {
...
}
...
}

Resources