Getting this weird error where any checked material UI switch becomes unchecked as I scroll down out of that data view.
Below is the Switch return with conditional rendering.
let freeTier = props.params.data.tier;
return freeTier === "FREE" ? (
<FormGroup>
<FormControlLabel
control={
<Switch
disableRipple
focusVisibleClassName={classes.focusVisible}
classes={{
root: classes.root,
switchBase: classes.switchBase,
thumb: classes.thumb,
track: classes.track,
checked: classes.checked
}}
{...props}
/>
}
/>
</FormGroup>
) : null;
And this is where I call the above into cellRendererFramework
cellRendererFramework: params => {
const handleClick = params => {
console.log(params.data);
};
return (
<PaypalSwitch
params={params}
data={params.data}
otherProps={this.props}
onClick={() => handleClick(params)}
/>
);
}
From what it seems like the grid re-renders? I'm getting this error.
ag-Grid: React Component 'cellRendererFramework' not created within
1000ms
Related
I am working in form using react-hook-form. This form use useFieldArray, it has to be dynamic.
Right now is very simple, it contains a react-select component with a few options and a textfield that get rendered depending on the option that the user select on the select component.
The problem I have is that the textfield component renders when the state updates, which is correct until I add a new group of element to the form. Since the textfield is listening to the same state it doesn't matter which select I use to render the textfield element, it gets rendered in all groups.
I am looking a way to specify which textfield should be rendered when the user change the select.
I the sandbox you can see what I have done. To reproduce the problem click on the "Add"-button and you will see two areas, each one with a select component.
When you choose "Other" in the select component a textfield appears, but not only in the area where the select was changed but in all areas.
How can I avoid that behavior?
https://codesandbox.io/s/vibrant-fast-381q0?file=/src/App.tsx
Extract:
const [isDisabled, setIsDisabled] = useState<boolean>(true);
const { control, handleSubmit, getValues } = useForm<IFormFields>({
defaultValues: {
managerialPositions: [
{
authority: 0,
chiefCategory: 0,
title: 0,
otherTitle: ""
}
]
}
});
useFieldArray implementation:
const {
fields: managerialPositionsFields,
append: managerialPositionsAppend,
remove: managerialPositionsRemove
} = useFieldArray({
name: "managerialPositions",
control
});
Here i update the state when the user select "Other title" in the select component:
const watchChange = (value?: number, i?: number) => {
let values: any = getValues();
if (values.managerialPositions[i].title === 3) {
setIsDisabled(false);
}
};
And here is where I render the button to create a new group of elements and the select component and the textfield that should be rendered if "isDisabled" is false.
{managerialPositionsFields.map((field, index) => {
return (
<Stack className="sectionContainer" key={field.id}>
<Stack horizontal horizontalAlign="space-between">
<StackItem>
<CommandBarButton
iconProps={{ iconName: "AddTo" }}
text="Add"
type="button"
onClick={() => {
managerialPositionsAppend({
authority: 0,
chiefCategory: 0,
title: 0,
otherTitle: ""
});
}}
/>
</StackItem>
</Stack>
<Stack horizontal tokens={{ childrenGap: 20 }}>
<StackItem>
<Label className="select-label requiredIkon">Title</Label>
<Controller
control={control}
name={`managerialPositions.${index}.title`}
render={({ field: { onChange, value, ref } }) => (
<>
<Select
className="react-select-container authoritySelect"
classNamePrefix="react-select"
placeholder="Select title"
options={titelList}
id={`managerialPositions.${index}.title`}
value={
titelList.find((g) => g.value === value)
? titelList.find((g) => g.value === value)
: null
}
onChange={(val) => {
onChange(val.value);
watchChange(val.value, index);
}}
/>
{
// this input is for select validation
<input
tabIndex={-1}
autoComplete="off"
style={{ opacity: 0, height: 0 }}
value={
titelList.find((g) => g.value === value)
? titelList
.find((g) => g.value === value)
.toString()
: ""
}
required={true}
//Without this console will get an error:
onChange={() => {}}
/>
}
</>
)}
/>
</StackItem>
{!isDisabled && (
<StackItem className="">
<Controller
name={`managerialPositions.${index}.otherTitle`}
control={control}
render={({
field: { onChange, name: fieldName, value }
}) => (
<TextField
label="Other title"
name={fieldName}
onChange={(e) => {
onChange(e);
}}
value={value}
/>
)}
/>
</StackItem>
)}
</Stack>
</Stack>
);
})}
I got TextField to work, now the Material UI Select will turn red if no selection is made but stays red after selection is made and won't let form submit. I'm using Yup as validation library.Maybe I keep using wrong Yup type I try String and array but I can't get it to work.
import {
makeStyles,
Box,
Select,
FormControl,
InputLabel,
MenuItem,
Typography,
} from "#material-ui/core";
import * as yup from 'yup';
import { yupResolver } from '#hookform/resolvers'
import { useForm, Controller } from "react-hook-form";
const FormFields = ({ typeOfInquiry, typeOfProviderSupplier, feedbackform }) => {
const schema = yup.object().shape({
typeofInquiry: yup.array().nullable().required(),
});
const { handleSubmit, control, reset, errors } = useForm();
return (
<Controller
style={{ minWidth: 220 }}
name="typeofInquiry"
render ={({ field: { ...field }, fieldState })=>{
console.log(props)
return ( <Select {...field} >
{typeOfInquiry.map((person) => (
<MenuItem key={person.value} value={person.value} >
{person.label}
</MenuItem>
))}
</Select>
)
}}
control={control}
defaultValue=" "
/>
<Typography className={classes.red}>{errors.typeofInquiry?.message}</Typography>
</FormControl>
</form>
);
}
You've to pass the ref to the TextField component.
Here is a working example
👉🏻 https://codesandbox.io/s/exciting-pateu-3n0i9
You should do something similar with Select.
Some examples with MUI: https://codesandbox.io/s/react-hook-form-v7-controller-5h1q5?file=/src/Mui.js
I'm trying to use Material UI Tabs for navigation. However, there are routes in my application that match none of the tabs. When I pass a value to the Tabs component that does not match any of the child tab values, I get a warning about an invalid value.
I created a hidden tab will a value of null as a work-around.
Is it possible to disable this warning about an invalid tab value?
Can tabs in Material UI have no selection?
Thanks
The value of the currently selected Tab. If you don't want any selected Tab, you can set this property to false.
From: https://material-ui.com/api/tabs/
What I ended up doing is creating a switch statement with valid tab values, and if windows.location.pathname doesn't match any of them have the default return false.
Example Routes:
class Routes extends Component {
render() {
return (
<Switch>
<Route path={'/test2'} component={Test2} />
<Route path={'/test3'} component={Test3} />
<Route exact path={'/'} component={Test} />
</Switch>
);
}
}
Example NavBar:
checkPathnameValue() {
const { pathname } = window.location;
switch (pathname) {
case '/test2':
case '/test3':
break;
default:
return false;
}
return pathname;
}
render() {
const { classes } = this.props;
const tabValue = this.checkPathnameValue();
return (
<div className={classes.root}>
<AppBar position={'static'}>
<Toolbar>
<Tabs value={tabValue}>
<Tab
label={'Test 2'}
value={'/test2'}
to={'/test2'}
component={Link}
/>
<Tab
label={'Test 3'}
value={'/test3'}
to={'/test3'}
component={Link}
/>
</Tabs>
</Toolbar>
</AppBar>
</div>
);
}
Seems like setting the value property of Tabs to false will not show any warning and will deselect all the tabs correctly.
Philip's solution works perfectly, here I am just removing the need for the switch case.
In my case, I only had one tab (Login) where I wanted no tab to be selected since it is a button rather than a tab.
Here's what I did to solve this:
<Tabs value={this.state.content !== "login" ? this.state.content : false} onChange={(event, newValue) => { this.setState({content: newValue}) }}>
<Tab value="home" label="Home" wrapped />
<Tab value="tab1" label="Tab 1" />
<Tab value="tab2" label="Tab 2" />
</Tabs>
on another part of my AppBar I had a Login button:
<Button onClick={(event, newValue) => { this.setState({content: "login"}) }}>Login</Button >
Similarly to Philips's answer, the key is in {this.state.content !== "login" ? this.state.content : false} which prevents Tabs from being rendered with "login" value. Instead, it is given the value "false" which is allowed and does not invoke the warning.
I also experienced this issue a while back and follow the same pattern.
E.g.,
return <Tabbar value={value ?? false} onChange={(event: React.ChangeEvent<{}>, value: any) => onChange(value)}>{tabs}</Tabbar>
Toggle Effect
To get a toggle effect the listener will need to be placed on the individual <Tab onClick/> events as <Tabs onChange> will not trigger when clicking the same button multiple times.
const Container = ()=>{
const [currentTab,setCurrentTab] = React.useState<string|false>('a')
const handleChange = (val: string) =>
setCurrentTab(val === currentTab ? false : val)
return <Tabs value={currentTab}>
<Tab value='a' label='a' onClick={()=>handleChange('a')}/>
<Tab value='b' label='b' onClick={()=>handleChange('b')}/>
</Tabs>
}
I'm trying to handle the selection of dynamically generated <option>'s in a <select> element. I understand that the onChange trigger is what i need to setState with but i can't seem to get her to work.
Here's what i've got going on:
See the Pen dynamic select by Archibald Hammer (#archaeopteryx) on CodePen.
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
const ITEMS = [
{ name: 'centos', text: 'centos', value: 'centosValue' },
{ name: 'ubuntu', text: 'ubuntu', value: 'ubuntuValue' },
]
const SelectComponent = (props) => (
<select name={props.name}>
{_.map(props.items, (item, i) => <Option
key={i}
name={item.name}
value={item.value}
text={item.text}
handleSelect={props.handleSelect}
/>
)}
</select>
)
const Option = (props) => (
<option
value={props.value}
onChange={props.handleSelect}>{props.text}</option>
)
class App extends React.Component {
constructor() {
super()
this.state = {
selected: ''
}
this.handleSelect = this.handleSelect.bind(this)
}
handleSelect(e) {
this.setState({selected: e.target.value})
}
render() {
return (
<div>
<SelectComponent
name="testSelect"
items={ITEMS}
handleSelect={this.handleSelect}
/>
<div>
<p>Selected: {this.state.selected}</p>
</div>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
This code does render the dropdown selector as expected but it isn't triggering the setState on the selected item. Any thoughts?
Also, does anyone have any pro-tips for troubleshooting this kind of problem? Any super slick dev-tools you know of or methods for finding out which props are being passed, etc?
The problem is that the options in a select element won't trigger any event, the change is happening in the select element not the option. All you have to do is pass the handleSelect method to the <select> component:
const SelectComponent = (props) => (
<select name={props.name}
onChange={props.handleSelect}
>
{_.map(props.items, (item, i) => <Option
key={i}
name={item.name}
value={item.value}
text={item.text}
handleSelect={props.handleSelect}
/>
)}
</select>
);
const Option = (props) => (
<option
value={props.value}
>{props.text}</option>
)
Sorry I forgot to add the live sample link:
https://codesandbox.io/s/31AyQ2woR
In terms of a tip for debugging, in this particular case just know that the event is triggered by the select component and not the option element ;). But the one I use all the time is React developer tools:
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
https://addons.mozilla.org/es/firefox/addon/react-devtools/
I'm using MaterialUI and I'm getting the error -
"Uncaught TypeError: Cannot read property 'stepIndex' of undefined"
In my Chrome console
I haven't had this error with any of the other Material-UI React Components. I am pretty new to React so if this is a stupid mistake please bear with me.
I cant seem to solve the problem.
I'm using React 5.3.1.
My code:
class HorizontalLinearStepper extends React.Component {
handleNext() {
const {stepIndex} = this.state;
this.setState({stepIndex: stepIndex + 1,finished: stepIndex >= 2});
};
handlePrev() {
const {stepIndex} = this.state;
if (stepIndex > 0) {
this.setState({
stepIndex: stepIndex - 1
});
}
};
getStepContent(stepIndex) {
switch (stepIndex) {
case 0:
return 'Select campaign settings...';
case 1:
return 'What is an ad group anyways?';
default:
return 'You\'re a long way from home sonny jim!';
}
}
render() {
const {finished, stepIndex} = this.state;
return (
<div>
<Stepper activeStep={stepIndex}>
<Step>
<StepLabel>Select campaign settings</StepLabel>
</Step>
<Step>
<StepLabel>Create an ad group</StepLabel>
</Step>
</Stepper>
<div style={contentStyle}>
{finished ? (
<p>
<a
href="#"
onClick={(event) => {
event.preventDefault();
this.setState({stepIndex: 0, finished: false});
}}
>
Click here
</a> to reset the example.
</p>
) : (
<div>
<p>{this.getStepContent(stepIndex)}</p>
<div style={{marginTop: 12}}>
<FlatButton
label="Back"
disabled={stepIndex === 0}
onTouchTap={this.handlePrev}
style={{marginRight: 12}}
/>
<RaisedButton
label={stepIndex === 2 ? 'Finish' : 'Next'}
primary={true}
onTouchTap={this.handleNext}
/>
</div>
</div>
)}
</div>
</div>
);
}
}
export default HorizontalLinearStepper;
Thanks!
<FlatButton
label="Back"
disabled={stepIndex === 0}
onTouchTap={this.handlePrev.bind(this)}
style={{marginRight: 12}}
/>
<RaisedButton
label={stepIndex === 2 ? 'Finish' : 'Next'}
primary={true}
onTouchTap={this.handleNext.bind(this)}
/>
You need to bind the handlers as they will be run in a different context and will loose the right meaning of this, so we bind the handlers to keep the right context of this.