i have this function in javascript:
export const commonRenderer = (option, useFormatter, hasSubLabel) => {
if (useFormatter && hasSubLabel) {
return (
<React.Fragment>
<FormattedMessage id={option.label} /><br /><FormattedMessage id={option.subLabel} />
</React.Fragment>
);
}
if (!useFormatter && hasSubLabel) {
return (
<React.Fragment>
{option.label}<br />{option.subLabel}
</React.Fragment>
);
}
if (useFormatter && !hasSubLabel) {
return (
<FormattedMessage id={option.label} />
);
}
return option.label;
};
and somehow i want to simplify this seems it looks really odd to me but im afraid of losing some cases. Any help?
Not sure if it's simpler or not, but you may try something like this:
export const commonRenderer = (option, useFormatter, hasSubLabel) => {
const Element = useFormatter ? FormattedMessage : React.Fragment;
const attr = useFormatter ? 'id' : 'children';
return (
<React.Fragment>
<Element {...{ [attr]: option.label }} />
{hasSubLabel && (
<React.Fragment>
<br />
<Element {...{ [attr]: option.subLabel }} />
</React.Fragment>
)}
</React.Fragment>
);
};
Related
I've made a list of refs for each of my components that is being rendered in a map, I am assigning a ref to a button within EditWebAppTypeForm and am trying to use it within MappedAccordion but it shows undefined? what can I do to make sure ref is set before passing it in the MappedAccordion component?
The information logged in addtoRefs function is correct, it shows -
(2) [button, button]
I've removed a lot of the code so its easier to read.
function Admin() {
const allRefs = useRef([] as any);
allRefs.current = [];
const addtoRefs = (e: any) => {
if (e && !allRefs?.current?.includes(e)) {
allRefs?.current?.push(e);
}
console.log(allRefs.current); <-- Logs correct info
};
return (
<div className="adminContainer">
<Grid container spacing={2}>
<Grid item md={8} xs={12} sm={12}>
<div style={{ width: 725, paddingBottom: 150 }}>
{webAppTypes &&
webAppTypes.map((a: IWebAppType, index: number) => {
return (
<>
<Accordion
key={a.id}
defaultExpanded={a.id === 0 ? true : false}
>
<AccordionDetails>
<EditWebAppTypeForm
key={a.name}
setWebAppTypes={setWebAppTypes}
IWebAppTypeModel={a}
ref={addtoRefs} // <-- returning ref to add to list
/>
<MappedAccordion
waobj={a}
key={a.id}
setWebAppTypes={setWebAppTypes}
editRef={allRefs.current[index]} <-- using ref but showing undefined in MappedAccordion component
/>
</AccordionDetails>
</Accordion>
</>
);
})}
</div>
</Grid>
</Grid>
</div>
);
}
export default Admin;
EditWebAppTypeForm Component -
const EditWebAppTypeForm = (props: any, ref: any) => {
return (
<div className="editWebAppSContainer">
<form onSubmit={handleSubmit(onSubmit)} id="edit-app-form">
<button hidden={true} ref={ref} type="submit" /> // <-- Assiging ref to button
</form>
</div>
);
};
export default forwardRef(EditWebAppTypeForm);
type MappedAccordionProps = {
waobj: IWebAppType;
setWebAppTypes: Dispatch<SetStateAction<IWebAppType[]>>;
editRef: any;
};
function MappedAccordion({
waobj,
setWebAppTypes,
editRef,
}: MappedAccordionProps) {
const onSubmit = (data: FormFields) => {
console.log(editRef); // <-- Logs undefined
};
return (
<div>
<form onSubmit={handleSubmit(onSubmit)} id="environment-form">
</form>
</div>
);
}
export default MappedAccordion;
I would create an extra component WebAppTypeAccordion like this :
function WebAppTypeAccordion({a, setWebAppTypes}) {
const [formEl, setFormEl] = useState(null);
function handleRef(el) {
if (el) {
setFormEl(el)
}
}
return (
<Accordion defaultExpanded={a.id === 0}>
<AccordionDetails>
<EditWebAppTypeForm
setWebAppTypes={setWebAppTypes}
IWebAppTypeModel={a}
ref={handleRef}
/>
<MappedAccordion
waobj={a}
setWebAppTypes={setWebAppTypes}
editRef={formEl}
/>
</AccordionDetails>
</Accordion>
);
}
Then you can update the Admin component :
webAppTypes.map((a: IWebAppType) => (
<WebAppTypeAccordion key={a.id] a={a} setWebAppTypes={setWebAppTypes} />
))
The problem is I have two redux stores
items(Items Store
quotationItems(Quote Items.
When a product item is added to quotationItems I Would to show <RedButton title="Remove" /> or when the quotationItems is empty the <AddButton title="Add" /> should be shown.
using if statement tends to re-render the component and adds new components e.g: After adding a new product to quotation items, there will be a new <AddButton /> in FlatList Component.
interface IProps {
items: ItemInterface[];
documentItems: ItemInterface[];
onAddItem: any;
}
const ItemFlatList2: FC<Partial<IProps>> = ({
items,
documentItems,
onAddItem,
}) => {
const TestView = (itemCode) => {
documentItems.map((x) => {});
};
return (
<FlatList
data={items}
keyExtractor={(item) => item.itemCode.toString()}
renderItem={({
item: { itemCode, itemDescription, itemSellingPrice },
}) => {
return (
<div className={styles.itemContainer}>
<h4>Item Code: {itemCode}</h4>
<h4>Description: {itemDescription}</h4>
<div>
{documentItems.map((x) => {
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
})}
</div>
</div>
);
}}
/>
);
};
Other options I've tried:
I've also tried this, still doesn't work, the buttons will show up but after clicking on the first button, which will successful add the product item to quotation item store but when I try and click on the 2nd or 3rd button of `<AddButton title={'Add'} key={itemCode} /> I get no response.
<div>
{documentItems.length <= 0 ? (
<AddButton
title={"Add"}
key={itemCode}
onClick={() => onAddItem(itemCode)}
/>
) : (
documentItems!.map((x) =>
x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<
AddButton title={"Add"} key={itemCode} onClick={() => onAddItem(itemCode)} />
)
)
)}
</div>
This is the result of the modified code above:
Please add the return keyword.
documentItems.map((x) => {
return (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
);
)})
Or Just remove the curly braces.
documentItems.map((x) => (x.itemCode === itemCode ? (
<RedButton title={"Remove"} key={itemCode} />
) : (
<AddButton title={"Add"} key={itemCode} />
)))
Based on the updations that you did in the questions. It is an issue with the data in "documentItems". Since you only need 1 button per item code. Instead of map use find. It will return 1 item and fix this duplicacy.
I have this component that processes a card and when you click it redirects you to a past route but your OnClick is not working. I wonder if I could be going wrong
function Characters({
characters,
getAllCharacters,
filterCharacters,
}) {
const history = useHistory();
useEffect(() => {
characters.length === 0 && getAllCharacters();
}, [getAllCharacters]);
return (
<Fragment>
<Header />
<Search characters={characters} />
{ inputCharacters !== "" && filterCharacters.length > 0 ? (
<ListContainer>
{filterCharacters.map((characters) => (
<CardItem
onClick={() => {
history.push(`/characters/${characters.id}`, {
params: { characters },
});
}}
key={characters.id}
name={characters.name}
images={
characters.thumbnail.path + "." + characters.thumbnail.extension
}
/>
)}
</ListContainer>
Component CardItem:
export default function CardItem(props) {
return (
<Container url={props.images}>
<Content>
<p>{props.name}</p>
</Content>
</Container>
);
}
Because you are not using onClick in the CardItem. You just update like this:
<p onClick={props.onClick}>{props.name}</p>
If Container or Content support onClick, you cant put onClick={props.onClick} in this component like a prop
I have an If statement and returning the same component with the extra props based on the state. Any idea how to simplify this? Can I use recursion? Any idea?
iconRight is only difference.
renderInput = () => {
if (isLoading) {
return (
<Input
iconRight={(
<Spinner />
)}
autoComplete="off"
id="unique-id-2"
aria-autocomplete="both"
/>
);
}
return (
<Input
autoComplete="off"
id="unique-id-2"
aria-autocomplete="both"
/>
);
}
}
You can spread props onto the component:
renderInput = () => {
const props = {
autoComplete: 'off',
id: 'unique-id-2',
'aria-autocomplete': 'off'
};
if (isLoading) {
return (
<Input
iconRight={(
<Spinner />
)}
{...props}
/>
);
}
return (
<Input {...props} />
);
}
}
But i'd suggest changing your Input component to accept a loading prop and let the Input component handle that logic. It'll make your consuming code a lot easier to read also.
I think your function can be shortened to the following;
renderInput = () => (
<Input
iconRight={isLoading ? (<Spinner />) : null}
autoComplete="off"
id="unique-id-2"
aria-autocomplete="both"
/>
)
If you don't already, inside your Input component, you should check if the iconRight prop is not null, and only render it then.
You can try this:
renderInput = () =>(
<Input
iconRight={
isLoading && (
<Spinner />
)}
autoComplete="off"
id="unique-id-2"
aria-autocomplete="both"
/>
)
I wonder if it is a better way to DRY this code, have you guys any ideas?
The props are the same, just the component change...
render() {
const { input: { value, onChange }, callback, async, ...rest } = this.props;
if (async) {
return (
<Select.Async
onChange={(val) => {
onChange(val);
callback(val);
}}
value={value}
{...rest}
/>
);
}
return (
<Select
onChange={(val) => {
onChange(val);
callback(val);
}}
value={value}
{...rest}
/>
);
}
With:
let createElement = function(Component) {
return (
<Component onChange={(val) => {
onChange(val);
callback(val);
}}
value={value}
{...rest}
/>
);
};
you can do
let selectAsync = createElement(Select.Async);
let select = createElement(Select);
You can render them in the jsx part with {{select}} and {{selectAsync}}
P.S.: I didnt test this directly, but did something very similar a few days ago, so this approach should work. Note that Component must start with a capital letter.