React Native - Global screen outside React-Navigation - javascript

In my React Native application, I am using Redux to manage the global state of the app. This is what the render of my App.js looks like:
render() {
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
where <MainNavigator /> exports createAppContainer(createBottomTabNavigator({ ...my panel names })) and <Provider ... /> is imported from react-redux.
Everything works just fine. However, I would like to have a "global" screen which is independent of my navigator. I would like to have it mounted all the time immediately after the app has been launched and display it over the main navigator or hide it using Redux.
As an example, I am aiming to achieve something like the music screen on Spotify and SoundCloud which is always mounted while browsing through the whole app. Or as another example, the video screen on YouTube which serves the same purpose as the previous examples.
I have tried the following in my App.js:
render() {
return (
<Provider store={store}>
<GlobalScreen />
<MainNavigator />
</Provider>
);
}
However, this would split my application into two halves where the upper half of the screen displays the <GlobalScreen /> component and the lower half displays the <MainNavigator /> component.
How can I achieve a "global" screen, independent of React Navigation, which can overlay it or hide itself using Redux?
P.S. I am not sure if this can be achieved by having the screen outside React Navigation. But I am open to any suggestions which could help me achieve my desired goal.

You can use a conditional to render either the GlobalScreen or the MainNavigator.
{someConditionBasedOnDataFromStore ? (
<GlobalScreen/>
):(
<MainNavigator/>
)
You probably want this logic inside a component which connects to the store to get the data for your conditional.

Related

React infinite render loop

I have created a component which needs to be individually displayed in multiple parts of the application - (inside of my navigation, on the home page and on a separate route). Therefore, I made all my action dispatches (api calls for reading data) inside a useEffect in this component:
useEffect(() => {
dispatch(time.read())
dispatch(pause.read())
dispatch(travel.read())
dispatch(distances.read())
}, [])
As you can see, this effect should only run once (when the component mounts).
Nowhere else do I dispatch these specific actions inside or outside of this component.
This component is rendered in 3 places (inside of the nav, in its own /time route and inside of the home component). I can render it inside of the navigation and on /time at the same time without a problem. It acts as it should (when I make changes from the navigation, they are mirrored on /time since it is all tied together with redux). Finally there is the Home component.
When I first tried to render on the Home component, it took a while to load, then I tried opening it up from my mobile device through my v4 address (same network), it took too long to load. At this point my laptop started sounding like a helicopter, so I opened htop, memory usage and CPU usage was pretty much maxed out, I opened up my server logs and saw hundreds of requests per second. Here is how I render this component inside my Home component:
import React from "react"
import { useSelector } from "react-redux"
import { NavLink } from "react-router-dom"
import "./Home.css"
import {
FaCarCrash,
CgProfile,
AiOutlineShop,
TiVideo,
} from 'react-icons/all'
import Tracking from "../TimeTracking/Tracking" /*This is the component which causes all the toruble*/
export default function Home(props) {
const current = useSelector(state => state.user.currentUser)
return (
<div className="Home container">
<div id="links">
<NavLink to="/create_job/">
<FaCarCrash />
<p> CREATE JOB </p>
</NavLink>
<NavLink to={"/profile/" + current._id}>
<CgProfile />
<p> PROFILE </p>
</NavLink>
<NavLink to="/shop/view/">
<AiOutlineShop />
<p> SHOP </p>
</NavLink>
<NavLink to="/tutorials/">
<TiVideo />
<p> TRAINING </p>
</NavLink>
</div>
<Tracking /> /*This is the component which causes all the toruble*/
</div>
)
}
As you can see, nothing out of the ordinary - a simple functional component which doesn't even dispatch any actions. Only communication with redux here is the useSelector.
Here is where I render the Home component:
import React, { Fragment } from 'react'
import { Route } from 'react-router-dom'
/*...other imports...*/
import Home from "../components/Mobile/Home"
/*...other imports...*/
export default props => (
<Fragment>
/*...other routes...*/
<Route path="/home" component={Home} />
/*...other routes...*/
</Fragment> /* just a fragment wrapper around the routes for the user. This component is later rendered inside of my <Router> </Router>. This is done because the app has a lot of different routes. */
)
For some reason, this component keeps re-rendering over and over again, even if I do not interact with the application. I've checked to see if I am ever dispatching the same action from 2 places which could cause this issue, but I am not. I know I haven't given a lot to go on, but there is really not that much to show in this issue, I'm just hoping someone has encountered it before. I've dealt with infinite render loops before, they are usually obvious and easy to solve but this one is a bit odd.
I have solved my problem. I was actually using the old version of my RootRouter.js which had the Home component declared like so:
<Route path="/home" component={<Home user={this.props.user} />} />
And the Home component I was rendering was also an older version (it was a class component, not the function component I wrote in the question).
So here is what caused the issue:
the user prop is passed down when declaring the Home route, and it holds the value of state.user
Then in the Home component, I am using mapStateToProps and mapping the user to the props, and setting it to state.user.currentUser.
This causes the user prop to be declared once when rendering the component, then changing once the component is rendered, and then it will change again on the next render and so on. The reason why react did not respond to the render loop is because the user prop holds a different value for each render.

Create a custom Show Layout component

I'm trying to create a custom layout component, so I can design my Show page better.
I'm not sure why some things work and some don't. My plan is to use the Material-UI <Grid> component. I know that the Grid component doesn't pass the props, then I'm extending to my own:
export default function OrderShow(props) {
return (
<Show {...props}>
<CustomGrid>
<TextField source="shortId" label="Short ID" />
</CustomGrid>
</Show>
);
}
My CustomGrid component, which is just a draft yet, is cloning its children to pass the props:
function CustomGrid(props) {
return React.Children.map(props.children, (child) => React.cloneElement(child, props));
}
When I use it, the TextField receives the source, and renders it correctly. However the label is gone. I'm not sure why this happens.
Another problem is that when I try to use the CustomGrid with a ReferenceField, it doesn't work at all:
<Show {...props}>
<CustomGrid>
<ReferenceField source="user" reference="users" label="Name" link="show">
<FunctionField render={(record) => `${record.firstName} ${record.lastName}`} />
</ReferenceField>
</CustomGrid>
</Show>
I can see that the props are passed until the ReferenceField, but it's lost before reaching the FunctionField:
Is there a better way to customize the SimpleShowLayout and continue using the TextField and other components from react-admin?
Yes, there is a way. Simply put react-admin's SimpleShowLayout is a little bit more coplex then just passing the props to the children fields. That's why you have to recursively iterate until you reach a field and perform the stuff the react-admin does.
Finally I have decided to share some privately developed components in a new package I am building. I have added layouts which work both with Box and Grid material-ui's components. You can have a look here: ra-compact-ui

Storage-design for fetched data (globally?) & navigation

im new to JavaScript & React (and new to stackoverflow too) and I hope to get some ideas/ answers regarding the "logic" in implementing data-storage. I have/ had a dev-background in Obj-C / iOS few years back. So some general concepts in web-dev might not be familiar to me.
Hope I'm doing everything correct in my first post, never really used stackoverflow so far :)
So, basically I have a small project with a Sider und Content (using Ant Design Framework). In this project, im trying to figure out, how generell stuff has to be done, like Navigation through the views, data handling and so on.
In one Tab in the SideBar, i'm using a Table (also antd). If you press on one Item, it should show a detail-View with the props of that specific item (later it should be the form for data change/input).
My App.js:
<Content style=...>
<Switch>
<Route exact path="/tasks" >
<TasksTable
data={this.state.tasksData}
handleOnRowClicked = {this.handleOnRowClicked}
/>
</Route>
<Route exact path="/details">
<TasksDetail currentTask= {this.state.currentDetailTask}/>
</Route>
....
</Switch>
</Content>
In the TasksTable-Component:
...
const history = useHistory()
...
function handleOnRowClicked(record, rowIndex,event) {
console.log("Clicked on cell", event)
history.push("/details/")
props.handleOnRowClicked(record.id)
}
return(
<Table
dataSource={props.data}
onRow={ (record, rowIndex) => {
return {
onClick: event => {
handleOnRowClicked(record, rowIndex,event) }
}
}
}
>
<Column title="Name" dataIndex="name" key="name" />
<Column title="Kunde" dataIndex="client_name" key="client_name" />
<Column title="Fertigstellung bis" dataIndex="completion_date" key="completion_date" />
<Column title="HinzugefĆ¼gt" dataIndex="created" key="created" />
<Column title=""
dataIndex="remove_button"
key="remove_button"
render={removeButton => {
return <button key="ButtonKey" onClick={event => handleOnButtonClicked(event)}>Remove</button>
}}/>
</Table>
)
}
What i want to achive:
When clicked on one row of the table, i want to push another "view"/ Side to show details of selected row. Currently I'm doing this in the TasksTable-Component with history.push(). I'm wondering if this is the correct way of showing (detail-)views? Because I need to call props.myHandleCallbackFunc(selectedItem) in App.js to set a State names "currentlySelected" for then passing this state as prop to the pushed detail view.
Hope you get my point.
As far as I understood, I should use ... for automating the navigation-process. Problem is: As I use the Table from antdesign, I need to make this programatically. Thats why I have this workaround (at least it feels like one) with the onRow property... Is this the correct way of using the table? I have the feeling its not.
Problem with my workaround: I've rendered a Button into the Cells to deliver a delete-Button.
Problem there: Even if i click on the Button, it is registered as a click on the row and therefor pushes my detailview... I tried to differentiate with event.target.key but had no success.
Is it the right way of holding all data fetched by my api in the uppermost component and share it to sup-components via props? How can I update it from subcomponents then? Should every Subcomponent should get a handler via props to call back, when value changed?
Or in other words: Make the http-request from the sub-comp or send the changed data to the App-component and let it then trigger the HTTP-POST-request? I strongly guess its second choice.
Thank you so much, I appreciate every answer. Im trying to get things working since few hours now...
(Sorry for spelling mistakes, english is not my native one).
P.S: Actually I wanted to post this yesterday but took few extra hours for working through the "similar questions"... Still, open questions :D
Robin
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.
https://redux.js.org/introduction/getting-started

React component not reloading on history.push

I have an app with routes defined as follows:
<Route exact path="/:category/:product" component={ProductComponent} />
On category page, "/:category I have two buttons for each product, "Quick View" and "Full view", on click of which I want to move to product page, but also set the state for view like setView('quick') or setView('full').
I tried solving this by using onclick on buttons with the following code:
() => {
setView('quick')
history.push(`/${category.slug}/${product.slug}`)
}
here both are called, history api changes the url but component does not load for that url. I cannot use the react-router's <Link> component because I need to set the view before going to product page.
How can I use the history from react-router to perform page change like <Link> does?
Try this
Remove component and use render
<Route
exact
path="/:category/:product"
render={({ match }) => <ProductComponent key={match.params.product || 'empty'} />}
/>

React 2 components of same Class in a switch, moving from 1 to another, doesn't render again

So this is my Switch:
<Switch>
<Redirect exact from={`${match.url}`} to={`${match.url}/faq`} />
<Route exact path={`${match.url}/faq`} component={FAQPage} />
<Route path={`${match.url}/troubleshooting`} component={FAQPage} />
<Redirect from={`${match.url}/*`} to={`${match.url}/faq`} />
</Switch>
My FAQPage is a component that has a list, which shows data from REDUX, based on the type of the list:
let listType = 'troubleshooting';
if (this.props.match.url.indexOf('faq') !== -1) {
listType = 'faq';
}
Where I have a ListContainer which contains the react-semantic-UI List which will populate the data.
My problem is that, if I load a page, I will get back data, and show it, but when I move to the other page. It will not Re-render the listContainer, which means it will not fetch the data, so it will show empty.
If I force reload, then my second page will load, but when I move to the first one, I get the empty data.
IF I copy paste the code of my component, and create another component to put in the second Route of the Switch. Something like:
<Switch>
<Redirect exact from={`${match.url}`} to={`${match.url}/faq`} />
<Route exact path={`${match.url}/faq`} component={FAQPage} />
<Route path={`${match.url}/troubleshooting`} component={TroubleShootingPage} />
<Redirect from={`${match.url}/*`} to={`${match.url}/faq`} />
</Switch>
This will work, assuming because the component is a different one, so it's not reused, so it has to render completely.
My question is: Is it possible to force my FAQPage to reload, from the SWITCH?
So that it will always show the correct data? (I could just leave the code with 2 exactly the same .js files, but I do not want to have redundancy like that in the code.
I would like to help. Your scenario is little hard to understand, would you consider adding minimal example on codesandbox.io

Categories

Resources