Linking to specific mui-tab using react-router-dom - javascript

I have a set of tabs using material ui as the base. I want to be able to link to one of the tabs so that that tab is open when landing on the page. All very simple for me outside of the react application(add anchor).
I've searched for solutions but no good, I suspect I need to do something with the main router and path and pass a property that tells the tab which one to be open. Then simply add that property on the link to pass to the page?
I will link to the tab(from a different page) using <Link>
<Link to="/page/tabpage" title="Link to tab 2">Link to tab 2</Link>
I imagine I will need to pass a value from this link to the URL which then opens up that specific tab, something like:
<Link to="/page/tabpage#tab2" title="Link to tab 2">Link to tab 2</Link>
My app route path for this page looks like this:
import { Route } from 'react-router-dom';
<Route path={`${match.url}/page/tabpage`}
component={asyncComponent(() => import('./routes/tabpage'))} />
The tabpage looks like something like the following:(for the general idea)
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import Tabs, { Tab } from "material-ui/Tabs";
import { whenGapiReady } from "../../../util/gapiHelper";
function TabContainer({ children, dir, className }) {
return (
<div dir={dir} className={className} style={{ padding: 8 * 3 }}>
{children}
</div>
);
}
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired
};
class tabpage extends Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
}
componentWillMount() {
whenGapiReady(() => {});
}
handleChange = (event, value) => {
this.setState({ value });
};
render() {
return (
<div>
<Tabs
initialSelectedIndex={this.state.value}
value={this.state.value}
onChange={this.handleChange}
indicatorColor="primary"
textColor="primary"
fullWidth
scrollable
scrollButtons="on"
classes={{ root: styles.root }}
>
<Tab className="tab" label="Tab 1" />
<Tab className="tab" label="Tab 2" />
</Tabs>
<TabContainer dir={theme.direction}>TAB 1 CONTENT ECT..</TabContainer>
<TabContainer dir={theme.direction}>TAB 2 CONTENT ECT..</TabContainer>
</div>
);
}
}
const mapStateToProps = ({}) => {};
export default tabpage;

Use with redux or local storage to manage active tabs and their content.
Map your tabs and render links.
Then render different components in different routes with links matched paths.
You may find some of my code usefull: https://github.com/SimonMulquin/EventsPlanning/tree/master/client/src/ui

Related

Filter React Components via search bar using onChange

I have a react app with multiple components rendered on several different pages.
I built a search bar component, to be re-used and rendered on 5 different pages,
to filter thru the components contained on each page.
What I'm trying to achieve:
I want the search bar to filter whole components without using onClick buttons. It
needs to filter thru whole components with onChange the same way .filter() and onChange filters
thru an array list of text as the user types, and show only components that match keywords and hide the rest.
Issues I'm trying to solve in my current code:
The only way I can get whole components to filter, is to add and display them thru the SearchBar component itself,
but needs to operate and filter thru whichever the current pages components are instead of
using <Block /> in the SearchBar component to display them. SearchBar should only filter whichever
components are rendered on the current page. (Block was just used to test if it filters whole components)
The keywords in state of compList: will only work if they are the exact name of the component, but it
needs to be triggered with generic keywords like ["taco", "burrito"] etc. (somehow reference the keywords to
the actual components maybe?)
For some reason, if the user backspaces, all of the components in the <Block /> disappear. That
doesn't need to happen..needs to display all components until a match is found. (somehow it's altering the states compList maybe?)
SearchBar Component
import React from 'react';
import './search-bar-styles.scss';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faSearch } from '#fortawesome/free-solid-svg-icons';
import { BreakfastBurritoRecipe, BreakfastTacoRecipe } from './recipe-box';
export class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
display: 'none',
value: '',
compList: ["BreakfastTacoRecipe", "BreakfastBurritoRecipe"]
};
this.showBar = this.showBar.bind(this);
this.hideBar = this.hideBar.bind(this);
this.userText = this.userText.bind(this);
};
showBar() {
this.setState({ display: 'block' });
};
hideBar() {
this.setState({ display: 'none', value: '' });
};
userText(event) {
const newList = this.state.compList.filter(item => item.includes(this.state.value))
this.setState({ value: event.target.value, compList: newList })
}
render() {
const _components = {
BreakfastTacoRecipe,
BreakfastBurritoRecipe
}
return (
<div>
<div id="searchButtonContainer" /*name={this.userText}*/>
<div id="searchButton" onClick={this.showBar}>
<FontAwesomeIcon icon={faSearch} />
</div>
</div>
<div id="searchContainer">
<div id="searchBox" style={{display: this.state.display}} onChange={this.showBar}>
<input value={this.state.value} onChange={this.userText} type="text" placeholder="Search.." />
<span id="closeSearchBtn" onClick={this.hideBar}>X</span>
<p>{this.state.value}</p>
<div>
{this.state.compList.map((component, index) => {
const Block = _components[component];
return <Block key={index} />
})}
</div>
</div>
</div>
</div>
)
}
};
Example of 1 of 5 pages where SearchBar will be rendered
import React from 'react';
import './breakfast-styles.scss';
import { Header } from './header.js';
import { BreakfastTitleBox } from './breakfast-title-box.js';
import { BreakfastTacoRecipe } from './recipe-box.js';
import { BreakfastBurritoRecipe } from './recipe-box.js';
import { OmeletteRecipe } from './recipe-box.js';
import { BiscuitsNGravyRecipe } from './recipe-box.js';
import { SausageBreakfastRollRecipe } from './recipe-box.js';
import { Footer } from './footer.js';
import ScrollArrow from './scrolltotop.js';
import { SearchBar } from './search-bar.js';
export class Breakfast extends React.Component {
render() {
return (
<div>
<ScrollArrow />
<Header />
<SearchBar /> <-----SearchBar being rendered on page
<BreakfastTitleBox />
<BreakfastTacoRecipe /> <----component to be filtered
<BreakfastBurritoRecipe /> <----component to be filtered
<OmeletteRecipe /> <----component to be filtered
<BiscuitsNGravyRecipe /> <----component to be filtered
<SausageBreakfastRollRecipe /> <----component to be filtered
<Footer />
</div>
)
}
}
Any suggestions? Examples?

how to add tab component and load pages in tabs in react js app?

I have a react js app, that i'm working on. I created a myfirstcomponent, which call a rest api and displays some json data. Now I want to expand my app and add more components, say mySecondComponent. but i want to make it such that i create tabs for this. so I researched and based on the tutorials, i created a tabs and a tab component as well. Now, i want to modify my render method in my app.js file , such that i can render my components within the tab component. so i tried the following, added the Tabs and Tab component inside the app div, now i need to show/hide the components based on the selected tab. . I'm new to react so, i want to make sure i am headed in right direction. is this the right way of doing this in reactjs?
render() {
return (
<div className="app">
<Tabs tabs={['first', 'second']} selected={ this.state.selected }
setSelected={ this.setSelected }>
<Tab isSelected={ this.state.selected === 'first' }></Tab>
<Tab isSelected={ this.state.selected === 'second' }></Tab>
</Tabs>
<myfirstcomponent className="compnent"/>
<mySecondcomponent className="compnent"/>
</div>
);
}
app.js file
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<myfirstcomponent className="App-com"/>
</div>
);
}
}
export default App;
Tabs
import React from 'react';
class Tabs extends React.Component {
render() {
return (
<div style={{ width: '30%' }}>
<ul className="nav nav-tabs">
{
this.props.tabs.map(tab => {
const active = (tab === this.props.selected ? 'active ' : '' );
return (
<li className="nav-item" key={ tab }>
<a className={"nav-link " + active + styles.tab} onClick={ () =>
this.props.setSelected(tab) }>
{ tab }
</a>
</li>
);
})
}
</ul>
{ this.props.children }
</div>
);
}
}
export default Tabs;
Tab.js
import React from 'react';
class Tab extends React.Component {
render() {
if (this.props.isSelected) {
return (
<div>
{ this.props.children }
</div>
);
}
return null;
}
}
export default Tab;
I don't think that approach is right. You should be using routing inside the tab component and links to the routes. Pluralsight site has a pretty good example on how to implement what you want to do.
https://app.pluralsight.com/guides/how-to-create-nested-tab-routes-with-react-router
NOTE: This might not be exact answer for your question.
I think you can use material-ui for creating the tabs component.
Here is a good example with the code. https://material-ui.com/components/tabs/
And also if you are new to react. I would encourage to learn/write functional components rather than class based components. Because in class based components the state management and props with the "this" keyword is very confusing.

Importing FontAwesome icons by string array in React.js

I'm having an array of sidebar elements in my React.js project where each element is represented as object which among others has its own FontAwesome icon defined as string, like e.g. fa-phone. Now there's a problem with FontAwesome's integration into React.js; each icon has to be separately imported and added to the library, according to their manual.
import * as React from 'react';
import { library } from '#fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
interface SidebarElement {
fa: string,
href: string,
title: string
}
interface SidebarElements {
elements: SidebarElement[]
}
export default class Sidebar extends React.Component<SidebarElements, {}> {
render() {
const elements = this.props.elements.map((element, key) => {
// tweak icon name so it matches component name...?
...
// the two lines below obviously won't work
import { element.fa } from '#fortawesome/free-solid-svg-icons'
library.add(element.fa);
return (
<li key={key} className="nav-item">
<a className="nav-link" href={element.href}>
<FontAwesomeIcon icon={element.fa} />
<span>{element.title}</span>
</a>
</li>
);
})
return (
<ul className="sidebar navbar-nav">{elements}</ul>
);
}
}
But the solution above obviously won't work, since imports have to happen at top-level and won't take the component name from a variable. Are there any alternative ways to import icons without exactly knowing them from the beginning? Or am I forced to import all icons at the same point I'm defining my sidebar elements?
I went with this same issue on a personal project I'm building. The first problem I found was related to how dynamically rendering the icon from a query?
Main app container:
import React from "react"
import Header from "../components/header"
import Navigation from "../components/navigation"
import Footer from "../components/footer"
import containerStyles from "./styles.module.less"
import { library } from "#fortawesome/fontawesome-svg-core"
import { fab } from "#fortawesome/free-brands-svg-icons"
library.add(fab)
const IndexPage = ({ data }) => (
<div className={containerStyles.home}>
<div className={containerStyles.menu}>
<Header />
<Navigation />
</div>
<Footer />
</div>
)
export default IndexPage
Also, my icons are part of the free-brand version so imported them to the library.
So the first thing I did was to import the library and create a pair of null variables on my child component, one for the prefix and the other one for the icon itself:
In my project, I'm consuming the data from an API endpoint, the query I built to get the information is the following:
Theoretically, all was set just for mapping the array and render each item as we normally do:
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix),
(faicon = document.node.icon),
]}
size="lg"
/>
But the child component was rendering nothing. Why was this? Simply because of both document.node.prefix and document.node.icon are returning strings so when the component mapped the data from the array, it ended trying to render something like this:
<svg data-prefix="'fab'" data-icon="'github'" >
Please note the single quotation mark wrapping the string
My solution to this was to use a replace() method with a regex to remove the wrapping quotations marks:
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix.replace(/'/g, "")),
(faicon = document.node.icon.replace(/'/g, "")),
]}
size="lg"
/>
Child footer component
import React from "react"
import { StaticQuery, graphql } from "gatsby"
import containerStyles from "../pages/styles.module.less"
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome"
let faicon = null
let faprefix = null
const Navigation = ({ data }) => (
<StaticQuery
query={graphql`
query FooterTemplate {
allStrapiLink {
edges {
node {
id
icon
url
prefix
}
}
}
}
`}
render={data => (
<>
<footer>
<p>Freddy PolanĂ­a {new Date().getFullYear()}</p>
<div className={containerStyles.links}>
{data.allStrapiLink.edges.map(document => (
<div key={document.node.id}>
<a
href={document.node.url}
rel="noopener noreferrer"
target="_blank"
>
<FontAwesomeIcon
icon={[
(faprefix = document.node.prefix.replace(/'/g, "")),
(faicon = document.node.icon.replace(/'/g, "")),
]}
size="lg"
/>
</a>
</div>
))}
</div>
</footer>
</>
)}
/>
)
export default Navigation
Now my icons are rendering from the endpoint's data. I hope this could help to solve your issue.

How do I access react-navigation from inside a component that isn't the HomeScreen?

I'm building a React Native app. I have imported createStackNavigator from react-navigation. I'm able to get it working on my Home screen - I click a button, it brings me to a new component. This is the code that I'm using to bring it into my Home.js
// src/components/Home/Home
export class Home extends Component {
render() {
return (
<React.Fragment>
<Button
title="Test button"
onPress={() => this.props.navigation.navigate('Roads')}
/>
<StatusBar />
<Header />
<Menu />
</React.Fragment>
);
}
}
const RootStack = createStackNavigator(
{
Home: Home,
Roads: Roads,
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
My Home page takes in a Menu which has a list of MenuItems. I am trying to get the MenuItems to jump to the appropriate pages. When I try to bring in the navigation inside MenuItem.js's render method, like so:
// src/components/Roads/Roads
render() {
const { navigate } = this.props.navigation;
console.log(this.props, "props is here");
I get the following error message:
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate').
Do I need to pass the navigator down in props to Menu.js and then to MenuItem.js? The docs give examples but it seems to be examples that assume you jam all your code into one file rather than across several components.
Have I set this up correctly?
When using a Navigator from react-navigation only the components you declare as Screens inherit the navigation prop (in your case Home and Roads)
This means that you will need to pass it as a prop to its children as you said:
<Menu navigation={this.props.navigation} />
<MenuItem navigation={this.props.navigation} />
In case anyone is wondering how to navigate from a component that isn't inside a Navigator then I suggest reading this part of the react-navigation documentation
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

react native - On button press load the whole screen with parent component

I am trying to load my parent component from child component on button press. But it's not rendering the parent components from btnPress method. I am not getting any error.
onButtonPress
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
btnPress Function
function btnPress(parent_id, id) {
const App = () => (
//I have tried this way but this didn't work. No any error, i can see log on console
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log(id);
AppRegistry.registerComponent('weightTraining', () => App);
}
full code(child component)
import React from 'react';
import { Right, Body, Thumbnail, Container, ListItem, Text, Icon } from 'native-base';
import { AppRegistry
} from 'react-native';
import Headerc from './headerc';
import ExerciseList from './exerciseList';
import Button from './Button';
const ExerciseDetail = ({ exercise }) => {
const { menu_name, menu_icon, parent_id, id } = exercise;
function NumberDescriber() {
let description;
if (menu_icon === 'noimg.jpg') {
description = `http://www.xxxxxx.com/uploads/icons/${menu_icon}`;
} else if (menu_icon === 'noimg.jpg') {
description = menu_icon;
} else {
description = `http://www.xxxxx.com/uploads/icons/${menu_icon}`;
}
return description;
}
function btnPress(parent_id, id) {
const App = () => (
<Container>
<Headerc headerText={'Fitness sdaf'} />
<ExerciseList pId={parent_id} mId={id} />
</Container>
);
console.log('-------------------------------');
console.log(id);
console.log('+++++++++++++++++++++++++++');
AppRegistry.registerComponent('weightTraining', () => App);
}
return (
<ListItem>
<Thumbnail square size={80} source={{ uri: NumberDescriber() }} />
<Body>
<Text>{menu_name}</Text>
<Text note> {menu_name} exercise lists</Text>
</Body>
<Right>
<Button onPress={() => btnPress(parent_id, id)}>
<Icon name="arrow-forward" />
</Button>
</Right>
</ListItem>
);
};
export default ExerciseDetail;
Please do let me know, if you need more information.
I would not suggest doing that way, it look totally anti-pattern and not.
better try with navigation or create a pattern like this
inside your index.js or index.android.js or index.ios.js
import App from './App' //your app level file
AppRegistry.registerComponent('weightTraining', () => App);
now in your app js file
export default App class extends React.Component{
constructor(props){
super(props);
this.state ={
component1:false,
component2:true,
}
}
btnPressed =()=>{
//handle state update logic here
}
render(){
if(this.state.component1) return <Component1/>
return <Component2/>
}
}
**** not the best solution available, play around and you will get best
To navigate from this component to your parent component unless you want to implement your own navigation which isn't recommended, you should look into one that's already built and adopted by many in the react-native ecosystem.
Some of the biggest ones:
React Native Navigation
React Navigation
React Native Router
I personally highly recommend option number 1, since it seems to be the most production tested and production ready implementation out there

Categories

Resources