turn on/off the method follow props in React - reactjs

I have developed a Component clickable.
and I want to make it disabled sometimes.
const MyComponent = ({disabled}) => {
return (
<button onClick={!disabled&&()=>console.log("CLICKED")}>btn</button>
)
}
const MyApp = () => {
return (
<MyComponent disabled={true} />
)
}
but it doesn't work.
and I have one solution what I don't want to use with.
const MyComponent = ({disabled}) => {
function handleClick(){
if(!disabled){
console.log("CLICCKED");
}
}
return (
<button onClick={handleClick}>btn</button>
)
}
Is there any solution better than what I found?

Why don't you use simple disabled property of button,
<button onClick={()=>console.log("CLICKED")} disabled={disabled}>btn</button>

Related

React, passed state is not changing in other component

I started with react and have a small problem
export default function TestComponent3({ typeId}) {
console.log('CHANGING HERE', { typeId});
const handleClick = () => {
console.log('NOT CHANGING HERE', { typeId});
console.log('NOT CHANGING HERE EITHER', typeId);
};
return (
<>
<Spectrum.Button variant="secondary" onClick={handleClick}>
CHANGING HERE {typeId}
</Spectrum.Button>
</>
);
}
In the other component the state of 'myProp' is changing by the UI dropdown.
Spectrum.Button content changes dynamically but console logs are stuck on default state option.
I probably messed something up and and It's easy fix.
EDIT://
Sibling with a dropdown
export default function TypeForm({ typeId, setTypeId }) {
return (
<div>
<Spectrum.Dropdown className="dropdown" placeholder="Choose type...">
<Spectrum.Menu onChange={setTypeId} slot="options">
{Types.map(type => { //types 0,1,2,3,4,5,6,7,8
return (
<Spectrum.MenuItem selected={typeId === type.id ? true : null} key={type.id} className="jsontest">
{type.name}
</Spectrum.MenuItem>
);
})}
</Spectrum.Menu>
</Spectrum.Dropdown>
)}
</div>
);
}
And Parent
export default function Form() {
const [typeId, setTypeId] = useState(0);
const setTypeIdFunc = e => {
setTypeId(e.target.selectedIndex);
};
return (
<TestComponent3 typeId={typeId} />
<TypeForm
typeId={typeId}
setTypeId={setTypeIdFunc}
/>
)
The props are passed down first from the parent component of TestComponent3 during the first render.
This means you would have to send handleClick from one "higher" component. Or use a shared state-like context provider.
I fixed it by changing Spectrum.Button to normal button.

Is this an OK way to handle 2 similar components?

I have two similar React components and the way that I handled them is by using the first component in the other one. It works, but not really sure if this is a bad practice or not.
const Component1({titles, handleClick, isSpecial}) => {
const toggle = (index) => {
handleClick(index)
}
return (
{titles.map((title, index) => (
<button onClick={() => toggle(index)}>Click me</button>
))}
)
}
import Component1
const Component2({titles, updateTitle}) => {
const alteredTitles = titles.map((title) => {
const alteredTitle = title.toUppercase();
return alteredTitle;
}
const handleClick = (index) => {
const alteredTitle = alteredTitles[index];
updateTitle(alteredTitle);
}
return (
<Component1 titles={alteredTitles} handleClick={handleClick} isSpecial={true}
)
}
Is this a poor way to do this? My other options of my current knowledge would be to duplicate most of the functionality of Component1 into Component2 or to add conditionals into Component1. The way I have done it makes my code feel more clean I guess, but I feel like it is the wrong way to go about it, but don't really know why.

Problem with using different onClick events in one button component

I wanted to make my components as reusable as it possible but when I started adding events the problems occured. I am using one button component in a lot of places in my app and I just change its name. It worked fine when I passed one onClick event to it (to change menu button name) but when I wanted to do the same with another button (to change cycle name) and when I passed second onClick event to the same button component the menu button stopped working. I tried to find solution but found only different topics. I know I could make a wrapper around the button and make onClick on the wrapper, but I think I am doing something wrong and there must be more elegant way to handle this.
Button component
export const Button = ({text, changeButtonName, changeCycle}) => {
return (
<AppButton onClick={changeButtonName, changeCycle}>
{text}
</AppButton>
);
};
Navbar component where cycle and menuu buttons are placed
export const Navbar = () => {
const menuButton = 'Menu';
const closeButton = 'Zamknij';
const [menuButtonName, setMenuButtonName] = useState(menuButton);
const changeButtonName = () => {
menuButtonName === menuButton ? setMenuButtonName(closeButton) : setMenuButtonName(menuButton);
}
const interiorButton = 'Interior →';
const structuralCollageButton = 'Structural Collage →';
const [cycleButtonName, setCycleButtonName] = useState(interiorButton);
const changeCycle = () => {
cycleButtonName === interiorButton ? setCycleButtonName(structuralCollageButton) : setCycleButtonName(interiorButton);
}
return (
<Nav>
<AuthorWrapper>
<AuthorName>
Michał Król
</AuthorName>
<AuthorPseudonym>
Structuralist
</AuthorPseudonym>
</AuthorWrapper>
<CycleButtonWrapper >
<Button text={cycleButtonName} changeCycle={changeCycle} />
</CycleButtonWrapper>
<MenuButtonWrapper>
<Button text={menuButtonName} changeButtonName={changeButtonName} />
</MenuButtonWrapper>
</Nav>
)
}
this is not a really reusable approach for a Button. For every new method name you would have to include in the props params and you could face something like:
export const Button = ({text, changeButtonName, changeCycle, changeTheme, changeDisplay})
the proper way to make it reusable would be by passing only one handler to your button:
export const Button = ({text, clickHandler}) => {
return (
<AppButton onClick={clickHandler}>
{text}
</AppButton>
);
};
fwiw, the reason you have problem is because at this code onClick={changeButtonName, changeCycle} you are passing multiple expressions with comma operator where the last operand is returned.
You cannot pass two functions to onClick. Either do a conditional check that call that function which is passed or make a wrapper function.
export const Button = ({text, changeButtonName, changeCycle}) => {
return (
<AppButton onClick={changeButtonName || changeCycle}>
{text}
</AppButton>
);
};
or
export const Button = ({text, changeButtonName, changeCycle}) => {
return (
<AppButton
onClick={() => {
changeButtonName && changeButtonName();
changeCycle && changeCycle();
}
}>
{text}
</AppButton>
);
};
update your code like
<AppButton onClick={()=> {
changeButtonName && changeButtonName();
changeCycle && changeCycle();
}}>
{text}
</AppButton>

Method within React functional component

Hello I had to add a method inside a component, that was stateless functional component. Now I am wondering if it can stay like this or should it be a class component now. My component:
const Pagination = ({ changePage }) => {
function changePageNumber(event) {
changePage(event.currentTarget.dataset.num);
}
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};
Can it be like this?
Yes, you can write methods like this. Also you can use arrow functions, like:
const Pagination = ({ changePage }) => {
const changePageNumber = event => {
changePage(event.currentTarget.dataset.num);
}
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};
Bonus: It's not necessary to name component that exported default, just:
export default ({ changePage }) => {
...
}
And in another file:
import AnyName from './Pagination'
You can change it to be like
const changePageNumber = (event) = () => {
changePage(event.currentTarget.dataset.num);
}
const Pagination = ({ changePage }) => {
return (
<button
className={css.button}
data-num={num}
onClick={changePageNumber}
>
{num}
</button>
);
};
changePageNumber is a function not a method. It is perfectly fine to stay there. It is there as a utility/helper function for that Pagination component. Such functions can be deployed to improve the readability of the code. Current state of your code perfectly fine.
Also you don't need to turn it into Class components, we use them when we think we need to store state, not to store methods.

How to place return code in a function: React

I currently have a react project I'm working on. My render method looks like this going into my return method:
render() {
let elements = [];
this.dropdownCounter().forEach(item => {
if(item != "attributeProduct") {
console.log('setting');
elements.push(
<Dropdown
title={this.state[item][0]['title']}
arrayId={item}
list={this.state[item]}
resetThenSet={this.resetThenSet}
/>
);
}
});
this.state.attributeProduct.map(attributeItem => {
elements.push(
<Dropdown
title={attributeItem.name}
arrayId='attributeMetaProduct'
list={
this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID)
}
resetThenSet={this.resetThenSet}
/>
);
});
return (
I have a lot of code going on in the render area due to different drop downs dependent on other methods. Is there a way that I can do something like this instead?
render() {
allMyPrereturnStuff()
return()
}
Then just place all this code in allMyPrereturnStuff()? I've tried creating this function and passing everything there but it doesn't work due to all the "this". Any ideas?
Yes, you can easily drop in normal javascript expressions into JSX:
return (
<div>
{this.renderStuff()}
{this.renderOtherStuff()}
{this.renderMoreStuff()}
</div>
);
You can even base it on flags:
const shouldRenderMoreStuff = this.shouldRenderMoreStuff();
return (
<div>
{this.renderStuff()}
{this.renderOtherStuff()}
{shouldRenderMoreStuff ? this.renderMoreStuff() : null}
</div>
);
Do note that it is often an anti-pattern to have render* methods in your components other than the normal render method. Instead, each render* method should probably be its own component.
Don't forget to bind your allMyPrereturnStuff() method in the constructor so "this" will work inside it.
constructor(props) {
super(props);
// ... your existing code
this.allMyPrereturnStuff = this.allMyPrereturnStuff.bind(this);
}
allMyPrereturnStuff = (params) => {
// ... all the code
}
However, you might want to consider breaking out the code to components, which is more Reacty way to do things.
For example, you could refactor this
this.state.attributeProduct.map(attributeItem => {
elements.push(<Dropdown
title={attributeItem.name}
arrayId='attributeMetaProduct'
list={
this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID)
}
resetThenSet={this.resetThenSet}
/>);
});
To something like (somewhat pseudocody):
const DropdownList = (props) => {
return (<Dropdown
title={props.attributeItem.name}
arrayId='attributeMetaProduct'
list={props.list}
resetThenSet={props.resetThenSet}
/>);
}
And in the original component's render function, have something like
render() {
return (this.state.attributeProduct.map(attributeItem => {
<DropdownList attributeItem={attributeItem}
list={ this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID) }
resetThenSet={this.resetThenSet}
/>);
}

Resources