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
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.
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();
};
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)} />}
</>
)
}
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`,
});
},
}),
];
}
}}
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.