How to hide sidebar in React using Ant Design - javascript

I'm trying to do a sidebar that hides on the clicking of an icon in the navigation bar. And I don't want to use classes, maybe I'm wrong doing it this way, but I want to keep it as much as possible. I've got an error that says:
(property) collapsed: boolean ';' expected.ts(1005)
In the const toggle:
const state = {
collapsed: true
};
const toggle = () => {
state.collapsed: !state.collapsed
};
const Sidebar = () => {
return (
<Layout.Sider collapsed={state.collapsed} style={{ backgroundColor: '#f0f0f0' }}>
...
</Layout.Sider>
)
}
In the navigation bar I got this:
<Button
type="primary"
shape="circle"
icon="user"
size={'small'}
style={{ marginLeft: '10px' }}
onClick={() => toggle}
/>
My layout:
const Layout = ({ children }: LayoutProps) => {
return (
<AntdLayout>
<AntdLayout>
<Header />
</AntdLayout>
<div>{children}</div>
<Sidebar />
</AntdLayout>
)
}
Thank you all!

There are two things what I found as an issue in your code. If you have functional component, you can use useState for updating boolean state. The other one is how you use onClick={() => toggle}, technically you are not calling the function, just returning.
I think you can try the following - creating a boolean variable with state hook:
const Sidebar = () => {
const [collapsed, setCollapsed] = useState(true);
return (
<Layout.Sider collapsed={state.collapsed} style={{ backgroundColor: '#f0f0f0' }}>
...
</Layout.Sider>
)
}
And in the button, you can use as the following - toggling the value of collapsed variable:
<Button type="primary"
shape="circle"
icon="user"
size={'small'}
style={{ marginLeft: '10px' }}
onClick={() => setCollapsed(!collapsed)} />
Read further here:
State hook
Function and Class components
If you are interested in further, I have prepared earlier a repository on GitHub to present toggling elements in class, functional or styled components. Find it here: norbitrial/react-toogle-class-on-click
I hope this helps!

Related

Override style declared in custom component or add multiple css styles using classes props from the Material UI Library

I customize a DataGrid component and pass classes as props from another component into my Custom Component. The problem here is that if I add one or more classes from another component. It will override all my style css from Custom component.
const DataGridBaseClasses = {
root: styles.DataGridBaseRoot,
cell: styles.DataGridBaseCell,
row: styles.DataGridBaseRow,
editBooleanCell: styles.EditBooleanCell,
columnHeader: styles.DataGridBaseColumnHeader,
columnSeparator: styles.DataGridBaseColumnSeparator,
};
const BaseDataGrid = (props) => {
const {
data,
columns,
rows,
rowPerPage,
className,
classes,
rowsPerPageOptions,
...others
} = props;
const [pageSize, setPageSize] = useState(rowPerPage);
return (
<DataGrid
{...others}
className={clsx(styles.DataGridBase, className)}
classes={{ ...DataGridBaseClasses, ...classes }}
disableVirtualization
disableColumnSelector
columns={columns}
rows={rows !== undefined ? rows : []}
loading={rows === undefined}
disableSelectionOnClick
pagination
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
rowsPerPageOptions={rowsPerPageOptions}
experimentalFeatures={{ newEditingApi: true }}
rowCount={data?.totalData}
checkboxSelection
/>
);
};
export default BaseDataGrid;
Another component:
<BaseDataGrid
className={styles.DataGrid}
classes={{
row: styles.DataGridRow,
cell: styles.DataGridCell,
}}
data={data}
columns={columns}
rows={productList}
rowPerPage={10}
rowsPerPageOptions={[10, 25, 50]}
/>
It can be solved using sx props like this. but it doesn't follow a common format.
<DataGrid
{...others}
className={clsx(styles.DataGridBase, className)}
classes={{ ...DataGridBaseClasses, ...classes }}
sx={{
"&": {
"& .MuiDataGrid-cell": {
"&:focus, &:focus-within": {
outline: "none",
},
},
},
}}
The expectation I want that is to override style declared in custom component or add another style css to the element. Can anyone help me solve this problem? Thank you so much.

React Native - how do you call the function of a child component from its parent (without triggering an infinite loop)?

I am trying to create a React Native e-commerce app where the featured products are shown, but then the user can view a list of categories via a sheet popping up from the bottom, which will load the products from said category.
I have managed to create such a bottom sheet using react-native-btr's BottomSheet. However, the function to show/hide the component (simply toggling a boolean in state) needs to be available to the component itself (to handle the back button and backdrop presses).
This is the component code:
const TestNav = (props, ref) => {
const [visible, setVisible] = useState(false);
const toggleVisible = () => {
setVisible(!visible);
};
useImperativeHandle(ref, () => toggleVisible());
return (
<BottomSheet
visible={visible}
//setting the visibility state of the bottom shee
onBackButtonPress={toggleVisible}
//Toggling the visibility state on the click of the back botton
onBackdropPress={toggleVisible}
//Toggling the visibility state on the clicking out side of the sheet
>
<View style={styles.bottomNavigationView}>
<View
style={{
flex: 1,
flexDirection: 'column',
}}
>
{DummyData.map((item) => {
return (
<Button
key={item.id}
title={item.name}
type="clear"
buttonStyle={styles.button}
onPress={() => console.log(item.name)}
/>
);
})}
</View>
</View>
</BottomSheet>
);
};
export default React.forwardRef(TestNav);
And here is the code for the screen where it's being used (it's called ChatScreen as I'm using it as a testing ground since I haven't implemented that feature yet)
import React, { useRef } from 'react';
import {SafeAreaView,StyleSheet,View,Text} from 'react-native';
import TestNav from '../components/TestNav';
import { Button } from 'react-native-elements';
const ChatScreen = () => {
const childRef = useRef(null);
const toggleBottomNavigationView = () => {
if (myRef.current) {
childRef.current.toggleVisible;
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<Text
style={{
fontSize: 20,
marginBottom: 20,
textAlign: 'center',
}}
>
Content goes here
</Text>
<Button
onPress={() => toggleBottomNavigationView()}
title="Show Bottom Sheet"
/>
<TestNav ref={childRef} />
</View>
</SafeAreaView>
);
};
export default ChatScreen;
However, this code has somehow triggered an infinite loop, as I get this 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.
How do I go about fixing this?
I think the issue lies with how you define the imperative handle. Hook callbacks are called each time the component renders and so () => toggleVisible() is called each render and creates a render loop. It should be passed a callback that returns the imperative functions/values to be made available to callees.
const toggleVisible = () => {
setVisible(visible => !visible);
};
useImperativeHandle(ref, () => ({
toggleVisible,
}));
In ChatScreen you then need to invoke the function. I'll assume the myRef in your snippet was a typo since it's not declared in the component and the usage appears to be similar to the guard pattern.
const toggleBottomNavigationView = () => {
childRef.current && childRef.current.toggleVisible();
// or childRef.current?.toggleVisible();
};

Is there a way to replace a component onPress in React Native?

In React-Native I am using this component <SimpleLineIcons name='heart' size={32} style={{position: 'absolute', left: 10}}/> I want so that when I click on it, it gets replaced with this component <AntDesign name='heart' size={32} color="red" style={{position: 'absolute', left: 10}}/>. How can I do this?
Create an item in the state, called toggleIcon. This will dictate what component to display. Then, to conditionally render the two component simply use a ternary operator like this:
export const MockComponent = () => {
const [toggleIcon, setToggleIcon] = React.useState(false);
return (
<>
// ...
{toggleIcon ? <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} /> : <AntDesign <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} />}
</>
)
}

How to set the width of material ui Popper to its container's width while setting disable portal as false

I am using material-ui popper.
I want to let the popper go put of container in y-direction. So I set disableportal={false}.
But after setting disableportal to false, when I give width: 100%, popper is occupying the entire browser's width instead of just it's container's width. I don't want the popper to go out of container in x direction but adjust it's width to the width of it's container.
How do I achieve this? Please check below code for reproducing the above issue.
import ClickAwayListener from '#material-ui/core/ClickAwayListener';
import Grow from '#material-ui/core/Grow';
import Input from '#material-ui/core/Input';
import MenuItem from '#material-ui/core/MenuItem';
import MenuList from '#material-ui/core/MenuList';
import Paper from '#material-ui/core/Paper';
import Popper from '#material-ui/core/Popper';
import React from 'react';
const items = [
'fsdfsdfsdfs',
'shosjsadsd',
'dsfdjhfdksfhdsf',
'fsdfhdhhhhhhhhh',
];
export function Test() {
const [value, setValue] = React.useState('');
const [anchorEl, setAnchorEl] = React.useState(null);
const handleChange = (event: any) => {
setValue(event.target.value);
};
const renderChildren = () => {
let renderItems = items;
if (value !== '') {
renderItems = items.filter((item: any) => item.toLowerCase().includes(value.toLowerCase()));
}
return renderItems.map((item: any) => {
return (
<MenuItem key={item}>
{item}
</MenuItem>
);
});
};
const onFoucs = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const popperTrans = ({ TransitionProps }: any) => {
return (
<Grow
{...TransitionProps}
style={{ transformOrigin: '0 0 0' }}
>
<Paper>
<MenuList>
{renderChildren()}
</MenuList>
</Paper>
</Grow>
);
};
const open = Boolean(anchorEl);
return (
<div style={{width: 1000, height: 500}}>
<ClickAwayListener onClickAway={handleClose}>
<div>
<Input
onChange={handleChange}
onFocus={onFoucs}
value={value}
placeholder='Search'
style={{width: '100%'}}
/>
<Popper
open={open}
anchorEl={anchorEl}
transition={true}
placement='bottom-start'
style={{zIndex: 10000, width: '100%'}}
>
{popperTrans}
</Popper>
</div>
</ClickAwayListener>
</div>
);
}
I'm sure there must be a better way to do this, but since I couldn't find one...
here is my workaround:
const [width, setWidth] = React.useState(DEFAULT_WIDTH);
useEffect(() => {
setWidth(document.getElementById('container')?.offsetWidth);
}, [document.getElementById('container')?.offsetWidth])
<Button id="container">{text}</Button>
<Popper
open={open}
placement="bottom"
style={{ zIndex: 1, width: `${width}px` }}
>
{...children}
</Popper>
I'm not sure about what is causing the issue for you. I can only point you to the example in the docs. I inspired my implementation from there and it worked like a charm. One difference that I see from your code sample is that the <ClickAwayListener> should be only wrapped around the <MenuList>. Hope it helps.
If you are using popper.js that allows you to override middlewares setting, you could do this:
Reference: Match reference width
A common feature of select dropdowns is that the dropdown matches the width of the reference regardless of its contents. You can also use size(){:js} for this, as the Rect{:.class}s get passed in:
// size fn is the part that does the trick
// you should alter other code to fit your usage scenario
popperProps={{
middlewares: (presetMiddlewares) => {
return [
...presetMiddlewares,
size({
apply({ rects, elements }) {
Object.assign(elements.floating.style, {
width: `${rects.reference.width}px`,
});
},
}),
];
}
}}

How would I dynamically append duplicate components in react, react-native

I am confused about how to properly dynamically add/create same components on button press for react native. I have used .map(()=>{}) on existing info to create components and then display the results.
Would I have to save each new component into a setstate array, then map that?
I looked a little into refs, but wasn't sure how that was better than just a setstate. The problem I see is if I want to update the value for each component, how would I go about that if their all originally duplicates?
Something along the lines of this:
class SingleExercise extends Component {
constructor(props) {
super(props);
this.state = {
objInfo: this.props.navigation.getParam("obj"),
currentSetSelected: 0
};
this.addSet = this.addSet.bind(this);
}
addSet = () => {
return (
<addSet />
)
}
render() {
const { navigation } = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ height: 80 }}>
<addSet />
<View>
<Button //here
large={false}
onPress={() => {
this.addSet();
}}
title={"add more"}
/>
</View>
</View>
</View>
);
}
}
const addSet = () => {
return (
<TouchableHighlight>
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
defaultValue={'test'}
onChangeText={(text) => this.setState({text})}
/>
</View>
</TouchableHighlight>
);
}
Here is what I would do:
Every click on addSet button should increment the AddSets counter like this:
<Button
large={false}
onPress={() => {
this.setState({addSetsCounter: this.state.addSetsCounter});
}}
title={"add more"}
/>
After every state update your component will be re-rendered. So now, all you need to do is to forLoop in through that counter and return as many AddSets components as needed. A simple for loop with .push() inside would do.
Inside render, before return place something like that:
let sets =[];
for(let i =0;i<this.state.addSetsCounter;i++){
sets.push(<AddSets key="AddSets-{i}"/>);
}
Then simply render your {sets}.
I cannot fully test that right now, I wrote that from the top of my head, just play with the above, at least I hope it points you in a right direction.

Categories