I'm new to react and I'm trying to substitute a part of a jsx return I'm repeating in a react component, but there are slight differences in each repetition so I want to pass a simple boolean as a parameter to the variable so I can check those inside jsx. Here is what I've done so far:
function links(condition){
console.log("Condition is: " + condition);
return(<h1 className={`mr-10 ${ condition === true ? 'flex' : 'hidden' }`>Test</h1>);
}
const Navbar = () => {
return( <nav>
<div> {links.call(false)} </div>
<div> {links.call(true)} </div>
</nav>
); }
This returns Condition is: then Condition is: Undefined in the console. How can I achieve this?
Down there you can see the "proper" way of using react:
function Links({ condition }) {
return(
<h1 className={`mr-10 ${ condition === true ? 'flex' : 'hidden' }`>
Test
</h1>
);
}
const Navbar = () => {
return(
<nav>
<div>
<Links condition={false} />
</div>
<div>
<Links condition={true} />
</div>
</nav>
);
}
Note:
Better call you component with a caps: link => Link
To call a component use <Component {...props} />
The props (parameters) are passed as a object in the first parameter of the function
Related
i want to return jsx if some condition is true if not undefined should be returned.
below is my code,
const showInfo = (item) {
return (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: (condition === 'value1' || condition === 'value2' ) &&
showInfo(item) //should put this condition into showInfo method
})
}}
/>
);
}
what i am trying to do?
the above code works. but now i want to put the condition inside the showInfo method. so if condition is true return jsx and if condition is false should return undefined.
what i have tried?
I have tried something like below
const showInfo = (item) {
return
{(condition === 'value1' || condition === 'value2' ) ? <div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
: undefined
}
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: showInfo(item) //error here
})
}}
/>
);
}
but the above tried code, gives error "Type 'void' is not assignable to type 'ReactNode'" at actions statement.
could someone help me with this. i am not sure if i have used ternary operator properly. thanks.
EDIT
after trying one of the answers provided,
notify is a method that is returned from usehook
and it evaluates to the component below
const Something: React.FC<SomethingProps> = ({
description,
actions,
...props
}) =>
(
<Header>
<Title>{title}</Title>
</Header>
{(description ||actions) && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
);
here the component is displayed when the condition fails in showInfo component.
in showInfo i am returning undefined if condition fails but still in the Something component the is displayed even though i have {description || actions}
i am not sure what is happening here.what is the condition i have to check for actions to not display in this case
i have tried
{(description ||actions !== 'false') && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
and this works. i am wondering why i should specifically mention
actions !== 'false'
instead of actions only
could someone help me with this. thanks.
If you want to return jsx from function you should wrap them inside some component. In this case you cen use <React.Fragment> or just <>. Another problem which I can see is that you probably forgot about arrow in you arrow function. Also don't know from where variable names condition comes from.
const showInfo = (item) => {
return (
<>
{ condition === "value1" || condition === "value2" ? (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
) : undefined}
</>
);
};
Wouldn't it be better to use the useState or useEffect hooks?
I have a div tag I want to render only if renderCard() style overflow is scroll. I have tried a renderCard().style.overflow which does not seem to target this
Edit: renderCard added
const SearchCard = () => (
<button class="invisible-button" onClick={onSearchCardClick}>
//
</button>
);
const AnswerCard = () => (
<div className="results-set">
//
</div>
);
const renderCard = () => {
if (card && card.answer) {
return AnswerCard();
} else if (card) {
return SearchCard();
}
return null;
};
<React.Fragment>
<div id="search-results">{renderCard()}</div>
{renderFollowup ? null : (
<React.Fragment>
<div id="search-footer">
{
(renderCard().style.overflow = "scroll" ? (
<div className="scroll-button">
<a href="#bottomSection">
<img src="images/arrow_down.svg" alt="scroll to bottom" />
</a>
</div>
) : null)
}
</div>
</React.Fragment>
)}
</React.Fragment>
);
};
To do this you need a ref to the actual DOM element rendered by renderCard().
renderCard() here returns a React element which doesn't have the style property or any other DOM properties on it - it's just a React representation of what the DOM element will eventually be once rendered - hence you need to get the actual DOM element via a ref where you'll have access to this and other properties.
Example code below using useRef to create the ref that will be attached to the element with the style you need to access. Note how useEffect is used to access the ref's value because it's only available after the first render when the DOM element is present.
const Example = () => {
const ref = React.useRef()
React.useEffect(() => {
alert('overflow value is: ' + ref.current.style.overflow)
}, [])
return (
<div ref={ref} style={{ overflow: 'scroll' }}>hello world</div>
)
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Refactor the cards to be actual rendered components, pass the ref and attach to the elements
const SearchCard = ({ overflowRef }) => (
<button ref={overflowRef} class="invisible-button" onClick={onSearchCardClick}>
//
</button>
);
const AnswerCard = ({ overflowRef }) => (
<div ref={overflowRef} className="results-set">
//
</div>
);
const RenderCard = ({ overflowRef }) => {
if (card && card.answer) {
return <AnswerCard overflowRef={overflowRef} />;
} else if (card) {
return <SearchCard overflowRef={overflowRef} />;
}
return null;
};
In the component rendering them, create a ref using either createRef or useRef react hook if it is a functional component
const overflowRef = createRef();
or
const overflowRef = useRef();
Pass the ref to RenderCard, to be passed on
<RenderCard overflowRef={overflowRef} />
And then check the overflow value as such
overflowRef.current.style.overflow === "scroll"
With the approach above you might want to refactor some of the HoC components and pass a function from parent to child that returns the ref of the element to be accessed also I am not sure of the scope of the block of code where you are executing the ternary.
Perhaps a simpler hack is to rely on using a useState hook with vanilla DOM selectors and passing that into the or even better, just add it to the stateless func component that wraps React.Fragment as a hook:
const myComponent = () => {
const [hasScrollOverflow, setHasScrollOverflow] = useState(false);
React.useEffect(() => {
const element = document.querySelector(".results-set");
const elementStyle = element.style;
if (elementStyle.getPropertyValue('overflow') === 'scroll') {
setHasScrollOverflow(true);
}
}, [])
return (
<React.Fragment>
<div id="search-results">{renderCard()}</div>
{renderFollowup ? null : (
<React.Fragment>
<div id="search-footer">
{hasScrollOverflow ? (
<div className="scroll-button">
<a href="#bottomSection">
<img src="images/arrow_down.svg" alt="scroll to bottom" />
</a>
</div>
) : null}
</div>
</React.Fragment>
)}
</React.Fragment>
);
};
I can see item and item id as it loops to render on the screen but i don't see the value of id when i click on any of the Tile where Tile is a div and react styled component.
class CategoryOffers extends React.Component {
passidtopointscreen =(id)=>{
console.log("id is", id);
localStorage.setItem('points_id',id);
this.props.history.push('/marketplacepoints')
debugger
}
render() {
debugger
return (
<Wrapper>
{this.props &&
this.props.cards_data &&
this.props.cards_data.map(item => {
return (
<Tile onClick={(item)=>this.passidtopointscreen(item.id)}>
<ImageWrapper>
<Image src={item.logo} height={'24px'} width={'73px'} />
</ImageWrapper>
<CardString>{item.offer_summary}</CardString>
</Tile>
)
})}
</Wrapper>
)
}
}
onClick={()=>this.passidtopointscreen(item.id)}
while adding item there you create new instance for this keyword for no reason
By having the same argument-name as your already decleared argument (item), you overwrite the outer argument. There should be no reason for you here to use the event-argument, if I have understood your question correctly.
I would also suggest avoiding localstorage and instead make use of the state.
I made the component into functional one here:
import React from "react";
const CategoryOffers = ({history,cards_data}) => {
const passidtopointscreen =(id)=>{
localStorage.setItem('points_id',id);
history.push('/marketplacepoints')
}
return (
<Wrapper>
{
cards_data?.map(item => {
return (
<Tile onClick={(event)=>passidtopointscreen(item.id)}>
<ImageWrapper>
<Image src={item.logo} height={'24px'} width={'73px'} />
</ImageWrapper>
<CardString>{item.offer_summary}</CardString>
</Tile>
)
})}
</Wrapper>
)
}
}
I am trying to get this function to fire from on click call in a child component.
getTotalOfItems = () => {
console.log('anything at all?')
if (this.props.cart === undefined || this.props.cart.length == 0) {
return 0
} else {
const items = this.props.cart
var totalPrice = items.reduce(function (accumulator, item) {
return accumulator + item.price;
}, 0);
this.setState({
estimatedTotal: totalPrice
});
};
}
This on click is being fired from within a Cart component
<button onClick={() => {props.addToCart(item); props.getPrice.bind(this)} }>+</button>
The cart component is being added to the ItemDetails component here
export default class ItemDetails extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
render() {
return(
<div>
<Button
className="item-details-button"
bsStyle="link"
onClick={() => this.setState({open: !this.state.open})}
>
{this.state.open === false ? `See` : `Hide`} item details
{this.state.open === false ? ` +` : ` -`}
</Button>
<Collapse in={this.state.open}>
<Cart
getPrice={this.props.getPrice}
/>
</Collapse>
</div>
)
}
}
Finally the ItemDetails component is added into the app.js like so
render() {
return (
<div className="container">
<Col md={9} className="items">
<ProductListing products={this.props.initialitems} />
</Col>
<Col md={3} className="purchase-card">
<SubTotal price={this.state.total.toFixed(2)} />
<hr />
<EstimatedTotal
price={this.state.estimatedTotal.toFixed(2)} />
<ItemDetails
price={this.state.estimatedTotal.toFixed(2)}
getPrice={ () => this.getTotalOfItems() }
/>
<hr />
<PromoCodeDiscount
giveDiscount={ () => this.giveDiscountHandler() }
isDisabled={this.state.disablePromoButton}
/>
</Col>
</div>
);
};
If I remove the () = > before the this.getTotalOfItems() it fires the function on the onClick, however it causes an infinite loop of re-rendering out the app causing an error.
Is there anyway to fix this? I am a novice at React and this is one of my first projects using it. Any advice shall be appreciated.
Sorry if this isn't explained to well, I am happy to provide any additional information if required.
Thanks!
You have to trigger getPrice method, now all you do is binding this context. Instead of props.getPrice.bind(this) you should have: props.getPrice()
props.getPrice.bind(this) doesn't call the function it just binds 'this' to it.
You should use props.getPrice() instead, also you don't have to bind the context of a children to it.
Some additionnal tips/explanations :
You can rewrite all your functions calls like this one :
getPrice={ () => this.getTotalOfItems() }
to
getPrice={this.getTotalOfItems}
It will pass the function to the child instead of creating a function which trigger the function (same result, better performance)
But if you do this :
getPrice={this.getTotalOfItems()}
It'll trigger the function at each render(), causing an infinite loop if the function triggers a render() itself by calling this.setState()
I have this function
renderCompanies() {
if (this.props.companies)
return [
<div>
Dashboard hello <div>{this.renderProfile()}</div>
<div>
{this.props.companies.map(function(item, i) {
return (
<div>
<div
key={i}
onClick={item => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
</div>
<AddCompanyPopUp />
</div>
];
}
I want to loop though this.props.companies and render a list of items. I want a user to be able to click on a specific item and have the item be saved to state.
This function runs inside another funtion
renderEitherMenuOrCompanyList() {
if (this.state.currentCompany) {
return <Menu companies={this.state.currentCompany} />;
} else {
return <div>{this.renderCompanies()}</div>;
}
}
Both are already bound to this
this.renderCompanies = this.renderCompanies.bind(this);
this.renderProfile = this.renderProfile.bind(this);
this.renderEitherMenuOrCompanyList = this.renderEitherMenuOrCompanyList.bind(this)
The renderEitherMenuOrCompanyList function is being called inside the render react function/method.
My problem is that I cannot set the state from the renderCompanies .map function. I keep getting "Cannot read property 'setState' of undefined" . This should be simple but I have not been able to do it
Make sure the function given to map is bound as well, or an arrow function:
{this.props.companies.map((item, i) => {
return (
<div>
<div
key={i}
onClick={() => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
The function passed to this.props.companies.map isn’t an arrow function, so it creates a new this. Change it to an arrow function to preserve the this from outside of it.
this.props.companies.map( ( item, i ) => { ... } )
You’ve also named the argument to onClick item, but it’s actually the click event. You want the item already defined by the map function. Name the argument to onClick something else, or nothing, to avoid overwriting the item variable you actually want.
onClick={ () => { ... } }