show Picker item when value is true React Native - javascript

I have a component which use Picker from 'react-native' and in props I am receiving bool value showDefaultPickerItem to show or hide the Picker.Item. The problem is that doing this way doesn´t work, I receive the error
null is not an object evaluating child props
For sure the list has length grater than 1.
function PickerComponent(props){
const { selectedValue, onValueChange, list, label, valuekey, showDefaultPickerItem } = props;
return (
<Picker
selectedValue={selectedValue}
onValueChange={(value) => onValueChange(value)}
>
{showDefaultPickerItem &&
<Picker.Item label={"Select"} value={undefined} color ={gray}/>
}
{list.map(l => {
return <Picker.Item key={l[valuekey]} label={l[label]} value={l[valuekey]} />
})
}
</Picker>
)
}

I think this code is helpful to you.
function make_list(list, showDefaultPickerItem, valuekey, label) {
let listMap = list.map(l => <Picker.Item key={l[valuekey]} label={l[label]} value={l[valuekey]} />);
if (showDefaultPickerItem) {
listMap.unshift(<Picker.Item key="_default" label={"Select"} value={undefined} color={'gray'} />);
}
return listMap;
}
function PickerComponent(props){
const { selectedValue, onValueChange, list, label, valuekey, showDefaultPickerItem } = props;
return (
<Picker
selectedValue={selectedValue}
onValueChange={(value) => onValueChange(value)}
>
{make_list(list, showDefaultPickerItem, valuekey, label)}
</Picker>
)
}

Related

Material UI React - Autocomplete - How to reset the internal state

My goal is to reset the internal state of Autocomplete Material-UI's component.
My custom component is rendered N times in my cycle
{branches.map((branch, index) => {
return (
<BranchSetting
key={index}
index={index}
data={branch}
removeBranch={removeBranch}
/>
)
})}
branch is my hook state.
This is my removeBranch function:
const removeBranch = (index) => {
let listBranch = branches;
listBranch.splice(index, 1);
setBranches([...listBranch]);
}
Every time I delete an item to my array branch, everything works fine except the Autocomplete.
This is my BranchSetting component:
import React, { useState, useEffect } from "react";
import Autocomplete from '#material-ui/lab/Autocomplete';
const BranchSettings = ({ removeBranch, index, branchModify, data }) => {
const [ brands, setBrands ] = useState(data.brands);
const handleBrandSelected = (event, payload) => {
const values = payload.map(item => item.value);
setBrands(values);
}
useEffect(() => {
setBrands(data.brands);
}, [data])
return (
<>
<Autocomplete
id={'branch-brand'}
multiple
disableCloseOnSelect
options={carsBrand}
getOptionLabel={(carsBrand) => carsBrand.label}
onChange={handleBrandSelected}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option.value}
size="small"
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => {
return (
<TextField
{...params}
variant="filled"
fullWidth
label={'brand'}
/>
)
}}
/>
</>
)
}
export default BranchSettings
carsBrand it is my data source that in the example I avoided writing the population. It's an array
Everytime I try to delete an item, Autocomplete keep the state of the component ad the prev position.
I'm looking a way to reset all the internal state of Autocomplete component.
The status I refer to can be seen with the devToolBar
I'm looking a good way to keep the items selected properly or that every time the component has changed, rerender the Autocomplete component.
I resolved the problem.
The problem was that Autocomplete component need to input an array of objects with label and value keys.
In the function handleBrandSelected I saved into my brands status just the value. I should have saved the whole object because then it must be sent as input in Autocomplete with the props value.
And to handle the object I should have also used props getOptionSelected.
No problems with the remove function, and no problems with indexes. Only the values selected in inputs and compliant with the documentation were missing.
So this is the new code
import React, { useState, useEffect } from "react";
import Autocomplete from '#material-ui/lab/Autocomplete';
const BranchSettings = ({ removeBranch, index, branchModify, data }) => {
const [ brands, setBrands ] = useState(data.brands);
const handleBrandSelected = (event, payload) => setBrands(payload);
useEffect(() => {
setBrands(data.brands);
}, [data])
return (
<>
<Autocomplete
id={'branch-brand'}
multiple
disableCloseOnSelect
options={carsBrand}
getOptionLabel={(carsBrand) => carsBrand.label}
onChange={handleBrandSelected}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option.value}
size="small"
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => {
return (
<TextField
{...params}
variant="filled"
fullWidth
label={'brand'}
/>
)
}}
getOptionSelected={(option, value) => option.value === value.value}
value={brands}
/>
</>
)
}
export default BranchSettings
This problem is probably caused by using the array index as a key in <BranchSetting key={index}. I recommend that you add a unique id when creating the branch object, and use that id as a key instead. You can use performance.now() or a small lib like nanoid.
You can read more about the negative impacts of using an index as a key here.

value is NaN after parseInt() in react-native TextInput

I am trying to set an Int value from the state inside a <TextInput> by first turning it into string (<TextInput> can only receive a string) and then I want to be able to change the value and update the state value with the new <TextInput/> value. When I am trying to change the value inside the <TextInput/>
I get error:
undefined is not an object (evaluating 'this.state.keys.toString')
UPDATE:
I removed the this from this.keyInt and now I receive NaN on input update , the error is gone though
React-Native code:
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
keys: 0,
id: this.props.id
};
}
updateKeysWithInputHandler = (e) => {
keyInt = parseInt(e.target.value);
console.log(`keyInt is: ${keyInt}`);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={this.updateKeysWithInputHandler}
/>
</View>
);
}
Ok I fixed my problem thanks to this post and the help from Adam Azad which point me to my problem.
apparently react-native <TextInput/> dont use target and instead its use nativeEvent.text so I changed my code and it worked now.
Working code:
updateKeysWithInputHandler = (val) => {
keyInt = parseInt(val);
console.log(val);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={(event) => this.updateKeysWithInputHandler(event.nativeEvent.text)}
/>
</View>
);
}
}
Why keys: this.keyInt?
Shouldn't it be keys: keyInt?
this.setState({
keys: this.keyInt
// ___^^^^^
})
Remove the this keyword because it changes the context from the current scope to Counter scope, where keyInt variable is not defined.
updateKeysWithInputHandler = (event) => {
if (isNaN(event.target.value) || event.target.value.toString().length===
0) {
event.target.value = 0;
}
this.setState({
keys: parseInt(event.target.value)
})
}

Handle Sliders dynamically using slider from Material-UI

I want to generate a sliders dynamically according to user input, and don't know how to save values on change. Following is the code given of my implementation.
The problem is that I can't get value via event.target.value
// priceCities is an array of objects:
handlePrices(priceCities){
return priceCities.map( (cstate, index) => (
<li key={index} >{cstate.name} <Slider key={index} min={3} max={500} step={1} style={{height: 100}} axis="y"
defaultValue={5} id ={cstate.id} onChange={ this.handleSlider.bind(this,cstate.id )} value={this.state.values[cstate.id] } /> <span>{this.state.values[cstate.id]}</span> </li>
));
}
this.state = {
values: []
}
and onChange() method here:
handleSlider ( event,i ) {
// this.state.sliderValue[event.target.id] = event.target.value;
//console.log('handlerslider'+event.target.id+' '+event.target.value);
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
}
Finally I found solution by defining the onChange method like this :
onChange={(event,value) => this.handleSlider(event, value,currState.id )}
and the code of handleSlider function :
handleSlider (event, value,id) {
let values = [...this.state.sliderValue];
values[id] = value;
this.setState({sliderValue: values });
console.log('handlerslider'+value+' '+id);
}
2020 Answer with Hooks
Something simple like this will work:
<Slider
onChange={(_, value) =>
setState(value)
}
step={1}
min={1}
max={50}
value={value}
valueLabelDisplay="auto"
/>

Input not rerendering inside SimpleForm

i was using AOR 1.2.2 for site settings panel.
Tricky part was that those settings can have different types: string,int,bool, array of string, int etc
I managed to do this by connecting record from store and using this code:
const SettingsEdit = (props) => {
return (
<Edit actions={<SettingsEditActions {...props} />} title=
{<SettingsTitle />} {...props}>
<SimpleForm toolbar={<EditToolbar />}>
<TextField source="description" />
<DisabledInput elStyle={{ width: '100%' }} label="Default
value" source="defaultValue" />
{renderCountryValue(props)}
</SimpleForm>
</Edit>
);
};
const renderCountryValue = (prop) => {
const record = prop.record;
if (record) {
if (record.multilang) {
// countryValue will be a dict with locale keys
// TODO Multilang fields temporary disabled in restClient
return null;
}
// countryValue will be single value or array
if (record.schema.type === 'array') {
// countryValue will be single array
if (record.schema.items.type === 'string') {
return <LongTextInput format={v => v.join()} parse={v => v.split(',')} label="Value" source="countryValue" />;
}
if (record.schema.items.type === 'integer') {
return <LongTextInput format={v => v.join()} parse={v => v.split(',')} validate={validateIntegerArray} label="Value" source="countryValue" />;
}
}
// countryValue will be single value
if (record.schema.type === 'string') {
return <TextInput label="Value" source="countryValue" />;
}
if (record.schema.type === 'integer') {
return <NumberInput label="Value" source="countryValue" />;
}
if (record.schema.type === 'boolean') {
return <BooleanInput label="Value" source="countryValue" />;
}
return <LongTextInput label="Value" source="countryValue" />;
}
return <TextInput label="Value" source="countryValue" />;
};
It was working well untill i tried updating AOR to 1.3.1 then it stopped.
What i noticed is that in first render there is no record so it renders default TextInput but on second render when there is record it doesn't rerender this Input into correct type like NumberInput or etc.
I tried to debug it and programm come to place when it should render other Input but nothing happen on screen.
Any ideas or workarounds?
Have faced some issues myself when depending on record in props. I usually then set manual checks for the value and return null when no record is found in props.

ReactJS: How to dynamically render Material-UI's <MenuItem/> inside <DropDownMenu/>?

Using ReactJS + Material-UI, I have an array called colors and contains strings of different colors. Say for example the array colors has 3 color strings: "white", "blue", "green. Then I would like to render each color string has a <MenuItem/> inside a <DropDownMenu/> (http://www.material-ui.com/#/components/dropdown-menu). And once a <MenuItem/> is selected, I'd like to console log that particular color like, say chose "white": console.log("white").
So I used .forEach yet the does not show any strings and it is empty. What could I be doing wrong?
Here is the code:
constructor() {
super()
this.state = {
value: 1,
}
}
dropDownColorChange(event, index, value) {
this.setState({value: value})
//Not sure how to implement here dynamically based on array size. Would like to console.log the color string of the selected
}
render() {
var colors = ["white", "blue", "green"] //would be able to handle any array size
return (
<div>
<DropDownMenu
value={this.state.valueTwo}
onChange={this.dropDownColorChange}
>
{
<MenuItem value={1} primaryText="Select" />
colors.forEach(color => {
<MenuItem primaryText={color}/>
})
}
</DropDownMenu>
</div>
)
}
Thank you
You've almost got it right. You have to map over available colors and return a MenuItem for each color:
const colors = ['white', 'blue', 'green'];
class ColorChanger extends Component {
constructor() {
super();
this.state = {
selectedColorValue: 1,
};
}
handleColorChange(event, index, value) {
console.log(`You have selected ${colors[value]} color`);
this.setState({
selectedColorValue: value
});
}
render() {
return (
<div>
<DropDownMenu value={this.state.selectedColorValue} onChange={this.handleColorChange}>
{colors.map((color, index) =>
<MenuItem key={index} value={index} primaryText={color} />
)}
</DropDownMenu>
</div>
);
}
}
map (contrary to forEach) returns an array where each element is the return value of predicate function. In your case it returns a <MenuItem />.
I used the react hook to set the menu items on clicking my menu icon and I also set the value I want to pass to my action method.
const [menuItems, setMenuItems] = React.useState<IMenuItem[]>();
const [menuValue, setMenuValue] = React.useState<IMenuValue>();
const handleClickMenu = (
event: React.MouseEvent<HTMLElement>,
value: IMenuValue,
) => {
setMenuItems(value.menuItems);
setMenuTransaction(value);
setMenuAnchorEl(event.currentTarget);
};
return (
// ... code ...
<PositionedVertMenu
data-testid={`menu`}
open={Boolean(menuAnchorEl)}
anchorEl={menuAnchorEl}
onClick={(event: React.MouseEvent<HTMLElement>) => handleClickMenu(event, value)}
onClose={handleCloseMenu}
>
{menuValue &&
menuItems?.map((option, menuIndex) => (
<MenuItem
data-testid={`menu-item-${menuIndex}`}
onClick={() => option.action(menuValue, handleCloseMenu)}
>
<Typography>{translate(option.text)}</Typography>
</MenuItem>
))}
</PositionedVertMenu>
)

Categories