Related
I'm new and tring to create a simple app, where 5 objects sit like a list/index, and if you press one of them, the app navigates to each of the detailed pages.
In each of the pages, there are next/goback buttons. If you press the next button, then the app navigates to the next detailed page/object from the current page where you are. For example, if you are in the 3rd page, you go to the 4th page. If you are in the 4th page, then you go to 5th page and so on.
Here's the screens of the app.
This is Home.js
[(https://i.stack.imgur.com/RbRgZ.png)](https://i.stack.imgur.com/Yg0MH.png)
Here's the cords that I've created so far.
people.js
const people = [
{
id: 1,
name: "Clark",
},
{
id: 2,
name: "Terry",
},
{
id: 3,
name: "Vega",
},
{
id: 4,
name: "Ryu",
},
{
id: 5,
name: "Ken",
},
];
export default people;
App.js
import * as React from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import Home from "./Home";
import EachPage from "./EachPage";
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="EachPage" component={EachPage} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Home.js
import React from "react";
import { View, Text, FlatList, TouchableOpacity } from "react-native";
import people from "./people";
function Home({ navigation }) {
return (
<View>
<FlatList
data={people}
keyExtractor={(person) => person.id}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate("EachPage", item)}
>
<Text>{item.id}</Text>
<Text>{item.name}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
export default Home;
EachPage.js
import React from "react";
import { View, Text, Button } from "react-native";
function EachPage({ route }) {
return (
<View>
<Text>{route.params.id}</Text>
<Text>{route.params.name}</Text>
//If you press the Next button, you go to the next page.
//So, if the current page you visit is the 2nd page, then you go to the 3rd page after pressing the Next button.
<Button title="Next" />
<Button title="Back" />
</View>
);
}
export default EachPage;
I think I know this shouldn't be that difficult, but I couldn't achive this. Even though I've finished two full courses, I didn't find how to do this.
How would you implement the buttuns that have the function that navigate to another screen/object in the same array in this case?
Thank you.
I took out the disabled logic and now essentially the data is in circular navigation for both next and back.
import React, { useState, useEffect } from "react";
import { View, Text, Button } from "react-native";
import people from "./people";
function EachPage({ route, navigation }) {
const [next, setNext] = useState(null);
const [back, setBack] = useState(null);
const [currentIndex, setCurrentIndex] = useState(null);
useEffect(() => {
setCurrentIndex(people.findIndex((item) => item.id === route.params.id));
}, [route]);
useEffect(() => {
setNext(currentIndex + 1);
setBack(currentIndex - 1);
if (currentIndex === 0) {
setBack(people.length - 1);
}
if (currentIndex + 1 === people.length) {
setNext(0);
}
}, [currentIndex]);
return (
<View>
<Text>{route.params.id}</Text>
<Text>{route.params.name}</Text>
{/* //If you press the Next button, you go to the next page. //So, if the
current page you visit is the 2nd page, then you go to the 3rd page after
pressing the Next button. */}
<Button
title="Next"
onPress={() => navigation.navigate("EachPage", people[next])}
/>
<Button
title="Back"
onPress={() => navigation.navigate("EachPage", people[back])}
/>
</View>
);
}
export default EachPage;
This is one possible solution, I tried it and worked for me:
EachPage.js
import React from "react";
import { View, Text, Button } from "react-native";
import people from "./people";
function EachPage({ route, navigation }) {
let currentIndex = people.findIndex((item) => item.id === route.params.id);
return (
<View>
<Text>{route.params.id}</Text>
<Text>{route.params.name}</Text>
{/* //If you press the Next button, you go to the next page. //So, if the
current page you visit is the 2nd page, then you go to the 3rd page after
pressing the Next button. */}
<Button
title="Next"
disabled={currentIndex + 1 === people.length}
onPress={() =>
navigation.navigate("EachPage", people[currentIndex + 1])
}
/>
<Button
title="Back"
disabled={currentIndex === 0}
onPress={() =>
navigation.navigate("EachPage", people[currentIndex - 1])
}
/>
</View>
);
}
export default EachPage;
Hi so currently i am trying to iteratively build tabs and the tabpanels to show it's information but i cannot get what i want to be displayed. i have a list of names for each name create a tab and a tabpanel
(Tabs e.g)(Tabs:TabA, TabB, TabC)underneath each tab should be a panel with info
(panel e.g(TabPanel:Data1, Data2, Data3) respectively.
i know how to create the tabs but i cant get the data i want to show in the panels properly with my if statements.
like i want to say if Tabs === "TabA" return Data1
instead i keep getting Data1 on every tab. here is my code below if anyone can assist.
tabs.js
import React, { useState, useEffect } from 'react';
// MUI
import {
Grid,
Paper,
Tabs,
Tab,
} from '#material-ui/core';
import PropTypes from 'prop-types';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
// styling MUI
import {
makeStyles,
} from '#material-ui/core/styles';
// api
import { retrieveForumTabsInfo } from 'api/ForumsAPI';
//page
import MissionRiskAssessment from "../views/msap" //future view to display on respective tab
import ATOCard from "../views/card" //future view to display on respective tab
import ResponsibilitiesCard from "../views/card2" //future view to display on respective tab
import MatLvlCard from "../views/card3" //future view to display on respective tab
// styling
const useStyles = makeStyles((theme) => ({
gridContainer: {
paddingTop: "2rem",
paddingLeft: "2rem",
paddingRight: "2rem",
},
}));
function ForumTabs(props) {
//styling
const classes = useStyles();
// state
const [tabsInfo, setTabsInfo] = useState([]);
const [currentTabIndex, setCurrentTabIndex] = useState(null);
// handlers
const handleTabChange = (event, tabIndexSelected) => {
setCurrentTabIndex(tabIndexSelected);
}
// on Mount...
useEffect( () => {
// append Tableau .js script tag
appendTableauJsScript();
// gather tabs info
let tabs_info = retrieveForumTabsInfo( props.forumObj.forum_name );
console.log("loooooooooook Heeerrrrrreee")
console.log("tabs_info " + tabs_info[0] )
setTabsInfo(tabs_info);
// not set current tab to 1
setCurrentTabIndex( 1 );
}, []);
//TabPanel
//*************************************** */
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box p={3}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
//********************************************************** */
return(
<Grid
container
spacing={2}
justify="center"
className={classes.gridContainer}
>
<Grid container item xs={10} alignItems="center">
<Paper position="static">
<Tabs
value={currentTabIndex}
onChange={handleTabChange}
indicatorColor="primary"
>
{/* initial Header tab */}
<Tab
label={
<div justify="center">
<img
src={process.env.PUBLIC_URL + '/img/prism-icon-inapp.png'}
alt="prismlogo"
height={32}
/>
Risk By:
</div>
}
disabled
/>
{/* generate Tabs ---here is where the trouble starts i create the tabs and now i want an if statement like if tabInfo==='A' return tabpanel that shows "cat" in tab "A" only not every tab */}
{
tabsInfo.map( (tmp_tab_info) => {
return(
<Tab label={tmp_tab_info}/>
);
})
}
</Tabs>
{/* added the tab panel */}
{/* generate views per respective panel */}
<TabPanel value={currentTabIndex} index={currentTabIndex}>
what to place here??
</TabPanel>
</Paper>
</Grid>
</Grid>
);
}
export default ForumTabs;
Try something like this
Generate Tabs
<Tabs
...
/>
...
{
tabsInfo.map( (tmp_tab_info, index) => {
return(
<Tab label={tmp_tab_info.label} {...a11yProps(index)}/>
);
})
}
</Tabs>
Generate TabPanels
{
tabsInfo.map((tmp_tab_info, index) => {
return (
<TabPanel value={currentTabIndex} index={index}>
{...tmp_tab_info}
</TabPanel>
);
})
}
I am using the Shopify CLI NodeJS tutorial to get started.
https://shopify.github.io/shopify-app-cli/app/rails/commands/#generate
I've been easily adding Polaris Components but I'm having trouble figuring out how to get my app to go from loading index.js to loading another page.
I am an absolute noob to this, all I want to do is basically add a route and load it.
My Pages Folder contains
_app.js
index.js
customer_import.js
How can i create a link in my apps navigation that will load the customer_import.js content where the current index.js content goes.
_APP.js
import fetch from "node-fetch";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import ClientRouter from '../components/ClientRouter';
import App from "next/app";
import {AppProvider, Avatar, Icon, VisuallyHidden, ActionList, Frame, TopBar, Navigation} from '#shopify/polaris';
import { Provider } from "#shopify/app-bridge-react";
import "#shopify/polaris/dist/styles.css";
import translations from "#shopify/polaris/locales/en.json";
import {ArrowLeftMinor, ConversationMinor, HomeMajor, OrdersMajor, QuestionMarkMajor} from '#shopify/polaris-icons';
const client = new ApolloClient({
fetch: fetch,
fetchOptions: {
credentials: "include",
},
});
class MyApp extends App {
render() {
const { Component, pageProps, shopOrigin } = this.props;
const theme = {
colors: {
surface: '#111213',
onSurface: '#111213',
interactive: '#2e72d2',
secondary: '#111213',
primary: '#000',
critical: '#d82c0d',
warning: '#ffc453',
highlight: '#5bcdda',
success: '#008060',
decorative: '#ffc96b',
},
logo: {
width: 250,
topBarSource:
'https://speedsociety-cdn.s3-us-west-1.amazonaws.com/dist/images/branding/ss-logo-new-blk.svg',
url: 'http://speedsociety.com',
accessibilityLabel: 'Speed Society',
},
};
const navigationMarkup = (
<Navigation location="/">
<Navigation.Section
items={[
{
label: 'Back to Shopify',
icon: ArrowLeftMinor,
},
]}
/>
<Navigation.Section
separator
title="Speed Society"
items={[
{
label: 'Dashboard',
icon: HomeMajor,
},
{
label: 'Speed Society Customers',
icon: OrdersMajor,
},
]}
action={{
icon: ConversationMinor,
accessibilityLabel: 'Contact support',
}}
/>
</Navigation>
);
const userMenuMarkup = (
<TopBar.UserMenu
actions={[
{
items: [{content: 'Back to Shopify', icon: ArrowLeftMinor}],
},
{
items: [{content: 'Community forums'}],
},
]}
name="Admin"
detail="Speed Society"
initials="Nick K"
/>
);
const secondaryMenuMarkup = (
<TopBar.Menu
activatorContent={
<span>
<Icon source={QuestionMarkMajor} />
<VisuallyHidden>Secondary menu</VisuallyHidden>
</span>
}
actions={[
{
items: [{content: 'Community forums'}],
},
]}
/>
);
const topBarMarkup = (
<TopBar
showNavigationToggle
userMenu={userMenuMarkup}
secondaryMenu={secondaryMenuMarkup}
/>
);
return (
<div style={{height: '250px'}}>
<AppProvider
theme={theme}
features={{newDesignLanguage: true}}
i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
shopOrigin: shopOrigin,
forceRedirect: true,
}}
>
<ApolloProvider client={client}>
<Frame topBar={topBarMarkup} navigation={navigationMarkup}>
<ClientRouter />
<Component {...pageProps} />
</Frame>
</ApolloProvider>
</Provider>
</AppProvider>
</div>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
shopOrigin: ctx.query.shop,
};
};
export default MyApp;
index.js
import { Heading, Card, Layout, Page, List, TextContainer, Frame } from '#shopify/polaris';
const Index = () => (
<Page title="Speed Society Data Migration App">
<TextContainer>
<hr/>
</TextContainer>
<Layout>
<Layout.Section oneHalf>
<Card
title="Customer Import Status"
secondaryFooterActions={[{content: 'View Imported Customers'}]}
primaryFooterAction={{content: 'Import Customers'}}
>
<Card.Section title="Progress">
</Card.Section>
</Card>
</Layout.Section>
<Layout.Section oneHalf>
<Card
title="Entries Import Status"
secondaryFooterActions={[{content: 'View Imported Entries'}]}
primaryFooterAction={{content: 'Import Entries'}}
>
<Card.Section title="Progress">
</Card.Section>
</Card>
</Layout.Section>
</Layout>
</Page>
);
export default Index;
customer_import.js
import { Card, Layout, Page } from '#shopify/polaris';
const CustomerImport = () => (
<Page>
<Layout>
<Layout.Section oneHalf>
<Card>
<div>Put content here</div>
See Polaris docs
</Card>
</Layout.Section>
<Layout.Section oneHalf>
<Card>
Put content here
</Card>
</Layout.Section>
</Layout>
</Page >
);
export default CustomerImport;
Use Next-Router component to redirect between pages. in your scenario
import { useRouter } from 'next/router'
//other stuff
const route = useRouter();
routeMe = () => {
router.push('/customer_import')
Render{
return(
<Card
title="Customer Import Status"
secondaryFooterActions={[{content: 'View Imported Customers'}]}
primaryFooterAction={{content: 'Import Customers', onClick: {this.routeMe}}}
>)}
You need to use client-side routing using next-router with Shopify app bridge to dispatch navigation actions and listen for them to navigate to the appropriate page, please check this answer and this related answer might be helpful too.
I have an application has been written with React + Redux and Antdesign. My application is a dashboard app. So I used the layout in Ant design https://ant.design/components/layout/
When I click on side menus, the active menu gets bold which is fine. But I need when I refresh the page, it checks and detect the route and bold related menu item.
I have a Sidebar component which is stateful. Inside it, in componentDidMount I call a function which will dispatch an action from mapDispatchToProps. The reducer changes the state. But in HTML codes, in defaultSelectedKeys, I can not set the number of active menus.
Sidebar.js component:
import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux'
import { Switch, BrowserRouter, Route, Link } from 'react-router-dom';
// antd
import { Layout, Breadcrumb, Menu, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
// Helpers
import { Alert } from '../helpers/notifications';
// Components
import Home from '../components/Home';
// import Header from '../components/Header';
import NotFound from '../components/NotFound';
import PostsEditor from '../components/Posts/PostsEditor';
// Actions
import { setRouteActiveFlag } from '../actions/ui.action'
class Sidebar extends React.Component {
componentDidMount () {
const routes = {
'/' : 1,
'/posts' : 2,
'/logout' : 3
}
this.props.detectActiveRoute(setRouteActiveFlag({
routes:routes,
path:window.location.pathname
}))
}
render() {
const { selectedRoute } = this.props;
console.log(selectedRoute);
return (
<div>
<Layout>
<Sider
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
}}
breakpoint="lg"
collapsedWidth="0"
onBreakpoint={broken => {
console.log(broken);
}}
onCollapse={(collapsed, type) => {
console.log(collapsed, type);
}}
>
<div className="logo" >
Logo <br/><br/><br/>
</div>
<Menu theme="dark" mode="inline" style={{ lineHeight: '64px' }} defaultSelectedKeys={[selectedRoute.toString() || '1']}>
<Menu.Item key="1">
<Link to="/" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Home</span>
</Link>
</Menu.Item>
<Menu.Item key="2">
<Link to="/posts" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Posts</span>
</Link>
</Menu.Item>
<Menu.Item key="3">
<a href="/logout" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Logout</span>
</a>
</Menu.Item>
</Menu>
</Sider>
<Layout style={{ marginLeft: 200 }}>
<Content style={{ margin: '24px 16px 0', overflow: 'initial'}}>
<Breadcrumb style={{ margin: '0 0 20px 0' }}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/posts/:id?" component={PostsEditor} />
<Route component={NotFound}/>
</Switch>
<Alert stack={ { limit: 3 } } />
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
state: state,
props: ownProps,
selectedRoute:state.ui.selectedRoute || 1
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
detectActiveRoute: (obj) => dispatch(obj)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Sidebar)
ui.action.js
export const setRouteActiveFlag = (payload = 'global') => ({
type: actions.SET_ROUTE_ACTIVE_FLAG,
payload
});
ui.reducer.js
import { handleActions } from 'redux-actions';
import Immutable from 'seamless-immutable';
import * as actions from '../consts/action-types';
const initialState = Immutable({
requests: {},
selectedRoute:{}
});
export default handleActions({
[actions.SET_ROUTE_ACTIVE_FLAG]: (state, action) => {
if (action.payload.routes && action.payload.path && action.payload.routes[ action.payload.path ]) {
return state.set('selectedRoute', action.payload.routes[ action.payload.path ])
}else{
return state.set('selectedRoute', 1)
}
}
}, initialState);
Please help me find the best and simple practices.
There is no need to use redux, just use react-router to get current pathname and pass it to defaultSelectedKeys.
<Menu defaultSelectedKeys={[this.props.location.pathname]}>
...
.....
</Menu>
Look at this answer , if you don't know how to get pathname
The following answer assumes you are using hooks. I know that from your question you are not using hooks, but it could be useful for other people. This answer works not only when refreshing but also when pressing the back and forward buttons:
import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { Layout, Menu } from 'antd'
const { Sider } = Layout
const items = [
{ key: '1', label: 'Invoices', path: '/admin/invoices' },
{ key: '2', label: 'Service Details', path: '/admin/service-details' },
{ key: '3', label: 'Service Contract Details', path: '/admin/service-contract-details' },
{ key: '4', label: 'Cost Centers', path: '/admin/cost-centers' },
{ key: '5', label: 'Clients', path: '/admin/clients' },
{ key: '6', label: 'Vendors', path: '/admin/vendors' }
]
const Sidebar = () => {
const location = useLocation()
const history = useHistory()
const [selectedKey, setSelectedKey] = useState(items.find(_item => location.pathname.startsWith(_item.path)).key)
const onClickMenu = (item) => {
const clicked = items.find(_item => _item.key === item.key)
history.push(clicked.path)
}
useEffect(() => {
setSelectedKey(items.find(_item => location.pathname.startsWith(_item.path)).key)
}, [location])
return (
<Sider style={{ backgroundColor: 'white' }}>
<h3 style={{ paddingLeft: '1rem', paddingTop: '1rem', fontSize: '1.25rem', fontWeight: 'bold', minHeight: 64, margin: 0 }}>
Costek
</h3>
<Menu selectedKeys={[selectedKey]} mode='inline' onClick={onClickMenu}>
{items.map((item) => (
<Menu.Item key={item.key}>{item.label}</Menu.Item>
))}
</Menu>
</Sider>
)
}
export default Sidebar
Your sidebar will look as follows:
You can add any css in your menu by conditioning and adding a class just in this way.
<MenuItem className={ (this.props.location.pathname==='/yourRoute')? 'active' : '' } >
</MenuItem>
In case if you get any kind of undefined error then you can use the 'withRouter' HOC
in this way.
In your component where you want to get that location prop, you will first import
import {withRouter} from 'react-router-dom';
then you can export it in this way.
export default withRouter(YourComponent);
Final code can look somewhat similar to this
import React, {Fragment, Component} from 'react';
import {withRouter, Link } from 'react-router-dom';
class Menu extends Component {
render(){
const {pathname} = this.props.location;
return (
<Fragment>
<div id="sidebar-menu" className="sidebar-menu">
<ul>
<li className={(pathname==='/dashboard' || pathname==='/')?'active':''}>
<Link to="/dashboard">Dashboard</Link>
</li>
<li className={(pathname==='/properties')?'active':''}>
<Link to="/properties">Properties</Link>
</li>
</ul>
</div>
</Fragment>
);
}
}
export default withRouter(Menu);
I am new to react native. I am developing an android app in which I want to add a new tab on tab bar by clicking a button, like we do in browser. Please can anyone tell me how to do it??
It's pretty clear in the documentation here https://reactnavigation.org/docs/intro/nesting
Example code
import { TabNavigator } from "react-navigation";
class RecentChatsScreen extends React.Component {
render() {
return <Text>List of recent chats</Text>
}
}
class AllContactsScreen extends React.Component {
render() {
return <Text>List of all contacts</Text>
}
}
const MainScreenNavigator = TabNavigator({
Recent: { screen: RecentChatsScreen },
All: { screen: AllContactsScreen },
});
Please check this link. I suggest you to use nativebase to make it easier, because they can provide scrollable tab. Then place add button at the end of the scrollable tab. Here is the example of scrollable tab code:
import React, { Component } from 'react';
import { Container, Header, Tab, Tabs } from 'native-base';
import Tab1 from './tabOne';
import Tab2 from './tabTwo';
export default class TabsScrollableExample extends Component {
render() {
return (
<Container>
<Header hasTabs/>
<Tabs renderTabBar={()=> <ScrollableTab />}>
<Tab heading="Tab1">
<Tab1 />
</Tab>
<Tab heading="Tab2">
<Tab2 />
</Tab>
<Tab heading="Tab3">
<Tab3 />
</Tab>
<Tab heading="Tab4">
<Tab4 />
</Tab>
<Tab heading="Tab5">
<Tab5 />
</Tab>
</Tabs>
</Container>
);
}
}
Using native base library to add new tab in tab bar
you can click on 'Add Tab' button to add new tab
import { Container, ScrollableTab, Tab, Tabs, Text, Button } from 'native-base';
state={ category:[]}
renderTabButton() {
this.setState({category: [...this.state.category, (<Tab heading="Add Category">
<Text>Tab 1</Text>
</Tab>)]})};
render() {
return (
<Container>
<Tabs renderTabBar={() => <ScrollableTab />}>
{this.state.category}
</Tabs>
<Button
onPress={this.renderTabButton.bind(this)}>
<Text> Add Tab</Text>
</Button>
</Container>