Material-UI BottomNavigationItem URL - javascript

UI on a react component. I have a <BottomNavigationItem /> component. This actually renders as an <button>. How can I actually make it render/navigate to a URL?
class FooterNavigation extends Component {
state = {
selectedIndex: 0,
};
select = (index) => this.setState({selectedIndex: index});
render() {
return (
<footer className="mdl-mini-footer">
<Paper zDepth={1}>
<BottomNavigation selectedIndex={this.state.selectedIndex}>
<BottomNavigationItem
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>
</BottomNavigation>
</Paper>
</footer>
);
}
}

Simply you can just add containerElement={<Link to="/home"/>} don't forget to import Link from react-router-dom
So it will be like this:
<BottomNavigationItem
containerElement={<Link to="/home"/>}
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>

Related

How to make React page render only change component instead of the whole page?

So I have a toggle looking like this (see below), but the page always re-render the whole thing on the first time I click on the toggle.
export default function Toggle({isChecked, label}: Props) {
return (
<Wrapper>
<Switch isChecked={isChecked}>
<span />
</Switch>
{label}
</Wrapper>
)
}
Then another component which is using this Toggle component
export default function ToggleBox({isChecked, label, children}: Props) {
return (
<Wrapper>
<Toggle isChecked={isChecked} label={label} />
<Content>{children}</Content>
</Wrapper>
)
}
There is a layout
export default function Layout({...someprops bla bla, children}: Props) {
<Wrapper>
<DesktopImg>
<ImageWrapper>
<Image src={image.url} alt={`${headline} image`} layout="fill" />
</ImageWrapper>
</DesktopImg>
<div>
<Content>
{back && backButton}
<MobileImg>
<Image
src={image.url}
alt={`${headline} image`}
width={image.width}
height={image.height}
/>
</MobileImg>
{headline}
<P gutterSize="medium">{description}</P>
</Content>
<ChildrenContainer>{children}</ChildrenContainer>
</div>
</Wrapper>
}
Then finally the page which use the ToggleBox.
export default function Index({isChecked, label, children}: Props) {
const [check, setCheck] = useState(false)
return (
<Layout>
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
</Layout>
)
}
I kinda tried to use the React.memo method but it doesnt seem to work. Any suggestion to make the page not re-render the whole thing but just the toggle?
Move your state further down the tree, you want it to be as close to the component(s) it impacts as possible, again this will probably require breaking out into smaller components, for example, break out the following into a seperate component -
const NewToggleComponent = () => {
const [check, setCheck] = useState(false)
return (
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
)
}
remove state from the top level component, and use this component in your top level component -
...
<NewToggleComponent />
...

how to change state of a react component from another component

I saw dozens of examples but they are not working in my case, I want to update the page variable in "bookitem" component and rerender it. using gives an error ' Expected an assignment or function call and instead saw an expression no-unused-expressions'
import React from 'react'
import { Pagination, Container } from 'semantic-ui-react'
import bookitem from './Book_item'
const PaginationI = () => (
<Container style={{textAlign: "center", padding:'4rem'}}>
<Pagination defaultActivePage={5} totalPages={10} onPageChange={PageChange}/>
</Container>
)
function PageChange(event,data){
console.log(data.activePage);
<bookitem page={data.activePage}/>
};
export default PaginationI
//////////////////////////////////////////////////////////////////////////////////////////////////////
class bookitem extends Component{
constructor(props){
super (props);
this.state={
counter:0,
page:0,
data2:[]
};
}
componentWillMount(){
console.log(this.props.page)
axios.get('/books/'+this.state.page).then(res=>{console.log(res.data);this.setState({data2:res.data});})
console.log('aa')
console.log(this.state.data2)
}
genurl(isbn){
console.log(isbn)
let url='http://covers.openlibrary.org/b/isbn/'+ isbn + '-L.jpg'
return url;
}
render(){return(
<div>
<div>{this.state.page}</div>
<Container>
<div style={{padding:"1em 1em", textAlign: "right"}}>
<Card.Group itemsPerRow={3} stackable={true} doubling={true}>
{this.state.data2.map(card=>(
<Card href="#">
<Image src={this.genurl(card.isbn)} wrapped ui={false} />
<Card.Content>
<Card.Header>{card.title}</Card.Header>
<Card.Meta>
<span className='date'>Author:{card.author}</span>
</Card.Meta>
<Card.Content >
<Rating icon='star' defaultRating={card.avgrating} maxRating={5} />
</Card.Content>
<Card.Description>
{card.avgrating} Avg rating, {card.totalratings} total ratings.
</Card.Description>
</Card.Content>
<Card.Content >
<a>
<Icon name='pencil alternate' />
{card.reviews} Reviews
</a>
</Card.Content>
</Card>
))}
</Card.Group>
</div>
</Container>
</div>
)
}
}
export default bookitem
The problem is that you are not rendering the bookitem component at all. You have to manage the state of your activePage, pass it to the bookitem and actually render this component.
import React, { useState } from "react";
import { Pagination, Container } from "semantic-ui-react";
import BookItem from "./Book_item";
const PaginationI = () => {
const [activePage, setActivePage] = useState(0); // manage the state of activePage
function PageChange(event, data) {
setActivePage(data.activePage); // update the state in event handler
}
return (
<Container style={{ textAlign: "center", padding: "4rem" }}>
<BookItem page={activePage} /> {/* render your component */}
<Pagination
defaultActivePage={5}
totalPages={10}
onPageChange={PageChange} /> {/* pass event handler */}
</Container>
);
};
export default PaginationI;
Also you would have to rename the bookitem component due to collision with HTML tags like this
import React from "react";
class BookItem extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
page: 0,
data2: [],
};
}
componentWillMount() {
console.log(this.props.page);
axios.get("/books/" + this.state.page).then((res) => {
console.log(res.data);
this.setState({ data2: res.data });
});
console.log("aa");
console.log(this.state.data2);
}
genurl(isbn) {
console.log(isbn);
let url = "http://covers.openlibrary.org/b/isbn/" + isbn + "-L.jpg";
return url;
}
render() {
return (
<div>
<div>{this.state.page}</div>
<Container>
<div style={{ padding: "1em 1em", textAlign: "right" }}>
<Card.Group itemsPerRow={3} stackable={true} doubling={true}>
{this.state.data2.map((card) => (
<Card href="#">
<Image src={this.genurl(card.isbn)} wrapped ui={false} />
<Card.Content>
<Card.Header>{card.title}</Card.Header>
<Card.Meta>
<span className="date">Author:{card.author}</span>
</Card.Meta>
<Card.Content>
<Rating
icon="star"
defaultRating={card.avgrating}
maxRating={5}
/>
</Card.Content>
<Card.Description>
{card.avgrating} Avg rating, {card.totalratings} total
ratings.
</Card.Description>
</Card.Content>
<Card.Content>
<a>
<Icon name="pencil alternate" />
{card.reviews} Reviews
</a>
</Card.Content>
</Card>
))}
</Card.Group>
</div>
</Container>
</div>
);
}
}
export default BookItem;
First of all Bookitem must starts with capitalized letter. So instead of <bookitem /> you must have <Bookitem/>.
Now if you want to change state of a react component from another component, you have to pass a function from parent to child which will be called when you want to change the state. For example
const Compoent1 = () => {
const [state, setState] = useState(value)
.....
return <Component2 changeState={setState} />
}

Isolating a function when data is mapped in react

I have data being mapped as a repeater. But I need to isolate the opening function (It's an accordion). I'm still learning my way through React. Basically, the accordions load with the state for open: false Once the ListItem is clicked, the HandleClick function toggles the state to open: true. A simple concept, I just need to isolate it so that it works independently. Whereas right now they all open and close at the same time.
Here is the state in a constructor and function
constructor(props) {
super(props);
this.state = {
open: true,
};
}
handleClick = () => { this.setState({ open: !this.state.open }); };
Here is my mapping script in ReactJS
{LicenseItems.map((item, index) => (
<div key={index}>
<ListItem
divider
button
onClick={this.handleClick}>
<ListItemText primary={<CMLabel>{item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open}
timeout="auto"
unmountOnExit>
{item.content}
</Collapse>
</div>
))}
The in dictates whether it is open or not per MaterialUI-Next
Thanks in advance guys!
Not very pretty, but something like this should work:
constructor(props) {
super(props);
this.state = {
open: {},
};
}
handleClick = (idx) => {
this.setState(state => ({open: { [idx]: !state.open[idx]} }))
}
// in render
{LicenseItems.map((item, index) => (
<div key={index}>
<ListItem
divider
button
onClick={() => this.handleClick(index)}>
<ListItemText primary={<CMLabel>{item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open[index]}
timeout="auto"
unmountOnExit>
{item.content}
</Collapse>
</div>
))}
It would be better to create separate Components for that, which have their own open state.
You should create two components for that:
Accordions.js
import React from 'react'
import Accordion from './Accordion'
const Accordions = props => {
return (
props.LicenseItems.map((item, index) => (
<Accordion key={index} item={item} />
))
);
}
export default Accordions;
Accordion.js
import React, { Component } from 'react'
class Accordion extends Component {
constructor(props) {
super(props);
this.state = {
open: true,
};
}
handleClick = () => { this.setState({ open: !this.state.open }); };
render() {
return (
<div>
<ListItem
divider
button
onClick={this.handleClick}>
<ListItemText primary={<CMLabel>{this.props.item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open}
timeout="auto"
unmountOnExit>
{this.props.item.content}
</Collapse>
</div>
)
}
}
export default Accordion;

React passing argument by path with router and does not render properly

I have the problem in React, with passing id argument using path in router.
That is my code(index.js):
class App extends React.Component{
render() {
return (
<Router>
<div>
<Home>
<Switch>
<Route path="/mail/:id" component={Mail} />
<Route path="/mail" component={Mail} />
</Switch>
</Home>
</div>
</Router>
)
}}
Next, in Mail.jsx, I have the render function:
render() {
const { activeItem } = this.state;
return (
<Grid>
<Grid.Column width={4}>
<Menu fluid vertical tabular>
<Menu.Item name='mail1' active={activeItem === 'mail1'} onClick={this.handleItemClick}> {this.props.match.params.id}</Menu.Item>
</Menu>
</Grid.Column>
<Grid.Column stretched width={12}>
<div>
<Segment>
{this.state.mails.length > 0 && this.state.mails ? this.state.mails[0].name : null}
</Segment>
</div>
</Grid.Column>
</Grid>
)
}
The problem is, that when I write http://localhost:3000/mail I see the "this.state.mails[0].name" and no id ofcourse because I didnt pass one, but when I write http://localhost:3000/mail/5 I can see the number five on the page, but no "this.state.mails[0].name" anymore, why is that?
And getting data from json file:
componentDidMount(){
axios.get("db.json")
.then( (response) => {
console.log(response);
this.setState({
mails: response.data.mail,
total: response.data.mail.length
});
})
.catch(function (error) {
console.log('error');
console.log(error);
});
}
Edit, that is my render function in Home.jsx:
render() {
const { activeItem } = this.state;
return(
<div>
<div>
<MenuExampleSecondaryPointing/>
</div>
<div>
{this.props.children}
</div>
</div>
);
}
I found an answer from a friend, that is that simple as that:
" axios.get("db.json")" should be "axios.get("/db.json")"
because with "/" I search in all catalogs for that file

Passing props to a react component wrapped in withRouter() function

I am using React-Router v4 to navigate in my React app. The following is a component wrapped in the withRouter() function to make it able to change route on click:
const LogoName = withRouter(({history, props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
As you can see I pass the props to the component, which I need in order to change the class of the component. The problem here is that props is undefined in the <LogoName> component. I need to be able to change the class of this component when I click on another component, like this:
<LogoName className={this.state.searchOpen ? "hidden" : ""} />
<div id="search-container">
<SearchIcon
onClick={this.handleClick}
className={this.state.searchOpen ? "active" : ""} />
<SearchBar className={this.state.searchOpen ? "active" : ""}/>
</div>
Here is how I handle the click. Basically just setting the state.
constructor(){
super();
this.state = {
searchOpen: false
}
}
handleClick = () => {
this.setState( {searchOpen: !this.state.searchOpen} );
}
Is there a way for me to pass props to a component that is wrapped inside the withRouter() function or is there a similar way to create a component which has the ability to navigate with React-Router and still receive props?
Thanks in advance.
The problem is that while destructuring, you want to destructure props but you are not passing any prop named props to LogoName component
You can change your argument to
const LogoName = withRouter((props) => (
<h1
{...props}
onClick={() => {props.history.push('/')}}>
BandMate
</h1>
));
However you can still destructure the props like #Danny also suggested by using the spread operator syntax like
const LogoName = withRouter(({history, ...props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
You're close, just spread the props in your function signature as well:
const LogoName = withRouter(({ history, ...props }) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
This worked for me:
import {withRouter} from 'react-router-dom';
class Login extends React.Component
{
handleClick=()=>{
this.props.history.push('/page');
}
render()
{
return(
<div>
.......
<button onClick={this.handleClick()}>Redirect</button>
</div>);
}
}
export default withRouter(({history})=>{
const classes = useStyles();
return (
<Login history={history} classes={classes} />
)
});

Categories