react Rating component not working as expected - reactjs

I am trying to make the react rating component work but I am getting some strange behaviour. When I make a selection (3 stars for example) the stars don't stay selected. This is how I want to be able to do it: I have a parent component (handles the state, etc.) that calls a child component. Then the child component makes a call to the React-Rating component. The reason I want to do this is because I want to be able to make these rating components usable in different places; therefore no code duplication would be needed. Below is my code.
//THIS IS FROM MY PARENT COMPONENT.
<Ratings onClick={newRating => this.setState({ rating: newRating })}/>
//THIS IS MY CHILD COMPONENT THAT CALL THE RATING LIBRARY.
export const Ratings = ({ rating, onClick}) => (
<div className={styles.rateContainer}>
<Rating
empty={`fa fa-star-o fa-2x ${styles.rating}`}
full={`fa fa-star fa-2x ${styles.rating}`}
onClick={onClick}/>// THIS IS TO OVERRIDE THE onClick event on the react library
<div>
<p className={styles.comments} />
</div>
</div>
);
export default Ratings;
What am I doing wrong?

I stored my rating as a part of state and then set the initialRate to reflect that state. That made my ratings stick. I think this might be what you are looking for.
<div className={styles.rateContainer}>
<Rating
empty={`fa fa-star-o fa-2x ${styles.rating}`}
full={`fa fa-star fa-2x ${styles.rating}`}
onClick={ (rating) => this.setState({rating: rating})}
intialRate={this.state.rating}
<div>
<p className={styles.comments} />
</div>
</div>

Related

Display a specific component by its key

I have this WP API data that I'm fetching in to a component called "HostingCard". I am mapping through this component which renders three cards. While mapping through these I am assigning a key to each of them.
`{hostings.map((hosting) => (
<HostingCard key={hosting.id} hosting={hosting} setHosting={setHostings} />
))}`
Each card has one of these titles "Pro", "Standard" and "Basic".
I made it so that once a card is clicked a new component appears. This component is called "ContactFormular".
Now in the "ContactFormular" component I want to display the information that was on the card that was clicked. is this possible?
Here's the code that opens a new component once the card/button is clicked:
import ContactFormular from './ContactFormular'
const HostingCard = ({post, handleSubmit, hosting, setHosting, showContactDialog, setShowContactDialog, ssl, setPro, pro, key}) => {
return (
<div className='noselect hosting-options'>
<input type="radio" id={post.acf.hosting} name="startup" />
<div className='card selected-card'>
<form onSubmit={handleSubmit}>
<label for={post.acf.tilvalg} className='label'>
<div className='card-header'>
<h5>{hosting.acf.title}</h5>
<p>{hosting.acf.beskrivelse}</p>
</div>
<div className='pakke-detaljer'>
<div>
<p style={{display: hosting.acf.deltaljer ? 'block' : 'none'}}>{hosting.acf.deltaljer}</p>
</div>
</div>
<div className='button-header' onClick={() => setPro(false)}>
<button className='btn' onClick={() => setShowContactDialog(true)}>Vælg</button>
</div>
</label>
</form>
</div>
<ContactFormular key={hosting.id} post={post} hosting={hosting} setHosting={setHosting} showContact={showContactDialog} setShowContact={setShowContactDialog} handleSubmit={handleSubmit} ssl={ssl} pro={pro} setPro={setPro}/>
</div>
)
}
It looks like you're passing in showContactDialog as a parameter and using that to decide whether or not to show this new component right? That means you need showContactDialog to be true for only the one you clicked on, and false for the others. ie each component needs a different state variable. It looks like you might be sharing the same state variable between all components. It's impossible to tell unless you share a reproducible example

Avoid component update while changing state vaiable

Background
I have a React component which includes further two more components. I component includes a chart(build with react-charts) and the other is a simple input field. I initially make not visible but it become visible when someone clicks icon over there.
Issue, child rerenders when state changes
Now the problem is whenever I toggle this input field it automatically refreshes my graph. In fact when I type into my input field it also refreshes the graph. I think it rerenderes the graph as I update the state variable. I want to stop this behavior. Any suggestions on how can I do this.
Component Screenshot(https://i.imgur.com/zeCQ6FC.png)
Component Code
<div className="row">
<DealGraph ref={this.dealRef} />
<div className="col-md-4">
<div className="row">
<div style={style} className="col-md-12 bg-white border-radius-10 default-shadow">
<h3 className="sub-heading roboto" style={border}>
Create Deals
</h3>
<input
type="text"
name="deal"
className="form-control mgt-30"
value="Deal One"
readOnly
/>
<button
type="button"
onClick={this.showAddBlock}
style={button}
className="golden-button create-deal-button"
>
<i className="fa fa-plus"></i>
</button>
{addDealStatus ? (
<div className="col-md-12 add-deal-box pd-0-0">
<input
type="text"
className="form-control mgt-30 mgb-10"
name="add-deals"
placeholder="Add Deals"
/>
<button type="button" className="golden-button flex all-center">
Add
</button>
</div>
) : null}
</div>
</div>
</div>
</div>
Toggle function
showAddBlock=() => {
this.setState({addDealStatus:!this.state.addDealStatus})
}
use PureComponent
To stop a child component rerendering from it parent you should make the child a pure component.
import React from 'react';
class DealGraph extends React.PureComponent { // notice PureComponent
render() {
const { label, score = 0, total = Math.max(1, score) } = this.props;
return (
<div>
/* Your graph code */
</div>
)
}
}
Use the { pure } HOC from Recompose
You can use a functional component that is wrapped in the pure HOC from recompose
import React from 'react';
import { pure } from 'recompose';
function DealGraph(props) {
return (
<div>
/* Your graph code */
</div>
)
}
export default pure(DealGraph); // notice pure HOC
A quick solution would be to move the input to an own component.
A more sophisticated solution is adapting the shouldComponentUpdate function in your DealGraphcomponent like stated here React: Parent component re-renders all children, even those that haven't changed on state change
By Default while rendering every component react checks for shouldComponentUpdate .React Components dont implement shouldComponentUpdate by default.So either we can implement a shouldComponentUpdate. Or Make the child class as a pure component.

React Modal Component Using Functional Hooks

I am working on an app that requires a modal component, this component is currently encompassed with three props that are passed to callback three different modals in the app structure. What I need to achieve is when a button is clicked for the specific modal, add a unique class to the modal wrapper that will identify each modal when clicked, thus manipulating the css with selectors for those modals.
see below:
const NWmodal = (props) => {
return (
<Modal>
<Modalmain data-modal-type="this should pass in a unique id to its modal once button is clicked">
<div>
{props.ejectModal}
</div>
<div>
{props.timeModal}
</div>
<div>
{props.leaveModal}
</div>
<Close>
x
</Close>
</Modalmain>
</Modal>
);
}
<NWmodal
id="timeModal"
timeModal={<NWtimeModal/>} (callback for timemodal component)
/>
<NWmodal
id="leaveModal"
leaveModal={<NWleaveModal/>} (callback for leavemodal component)
/>
<NWmodal
id="ejectModal"
leaveModal={<NWejectModal/>} (callback for ejectmodal component)
/>
function Nav() {
return (
<ul>
<li>
<button
href="javascript:void(0)"
data-modal="timeModal"
onClick={event}>
Time Modal
</button>
</li>
<li>
<button
href="javascript:void(0)"
data-modal="leaveModal"
onClick={event}>
Leave Modal
</button>
</li>
<li>
<button
href="javascript:void(0)"
data-modal="ejectModal"
onClick={event}>
Eject Modal
</button>
</li>
</ul>
);
}
As a disclaimer: I'm still learning this React thing, but the approach I would likely take...
The useRef hook can be used to create variables, which may then in turn be associated with your modals as a reference. eg:
const ejectModalRef = useRef()
<NWmodal id="ejectModal" ref={ejectModalRef} />
The node will be accessible in the onClick JS code as 'ejectModalRef.current'.
(Don't forget to import useRef eg.: import React, { useRef } from "react";
HTH

Reactjs component - one vs many

I'm new to react and I'm trying to figure out the best way to make a component that can handle different scenarios. I'm not sure if the best practice would be to make multiple components or one component to handle it all.
Imagine a frontpage were you have 3 different entrances like recent products, blogpost or Instagram pictured. Each entrance use a component called featured and inside that component I should render either products, blogpost or Instagram pictures. Everything for the layout is the same, its just the items in the grid that needs to change. What would be the best way to solve this? one component with 3 different sub-components or 3 components with one for each type.
I know how to make 3 different components, but I'm not sure how to make one component to handle subcomponents.
This could be the component and the "grid-item--product" could also be a "grid-item--blogpost or "grid-item--Instagram" - "grid" could also be a "two-col" or "three-col".
<div className="featured">
<div className="featured--content">
<div className="grid four-col">
<grid-item--product />
</div>
</div>
</div>
and this could be where I would call the component and hopefully be able to handle which component should be rendered inside and what the grid should be for this feature.
<div className="frontpage-route">
<h2>Frontpage Route</h2>
<Featured />
</div>
Can you help me? I would love an example if possible.
Thanks.
It sounds like what you want is the children prop. You can add the children prop to Featured and just pass the correct children to it. See an example here:
const Featured = ({ children, numColumns = "one" }) => (
<div className="featured">
<div className="featured--content">
<div className={`grid ${numColumns}-col`}>
{children}
</div>
</div>
</div>
)
const App = () => (
<div className="frontpage-route">
<h1>Frontpage Route</h1>
<h2>Products</h2>
<Featured numColumns="two">
<grid-item--product />
<grid-item--product />
</Featured>
<h2>Blogs</h2>
<Featured numColumns="three">
<grid-item--blog />
<grid-item--blog />
</Featured>
<h2>Instagram</h2>
<Featured>
<grid-item--instagram />
<grid-item--instagram />
</Featured>
</div>
)
You can use consitional rendering and three boolean variables to display components.
e.g:
<div className="featured">
<div className="featured--content">
<div className="grid four-col">
{product && <grid-item--product />} //if product var is true this component renders
{blogpost && <grid-item--blogpost />} //if blogpost var is true this component renders
{instagram && <grid-item--instagram />} //if instagram var is true this component renders
</div>
</div>
</div>

React access Dom Nodes from this.props.children

Let's say I have a Card that contains a login Form
<Card>
<LoginForm/>
</Card>
How do I access the nodes from the Form within the Card render function?
<Form >
<input type="text" name="email"/>
<input type="password" name="password"/>
<input type="submit"/>
</Form>
Because what i´d like to do is to render the submitbutton not within the props.children context but render it wrapped outside of the given child!
render () {
return (
<div className="card">
<div className="inner">
{/* render Children */}
{this.props.children != undefined ?
<div className="childrenWrapper">
{this.props.children}
</div>
: ""
}
</div>
{/* render submit from login form here, not above */
</div>)
There are some components which actually do what I want. For example the Tabs component from react-toolbox. They somehow manage to render what's within the Tab (children) somewhere else
Just for instance
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
Which will lead to the following html
As you can see the children from the tab where rendered within their own section
I do not want to change anything on the Form to solve this problem, I would like to pass the Form into the Card and let the Card decide how the Form will be rendered within the card render function.
Since I'm trying to implement the Google Material Design Card component and just use it as a template there are more elements coming which will need to be split up and placed at the positions I want them to be. The thing is I could actually place the relevant HTML around the Form to get it as the Card I want it to be, but then I wouldn't need the component at all.
There are some decent answers here, but none of them directly answer your question. Therefore, even though you should refactor your code (as elucidated below), I am going to provide you a working solution:
class Card extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
console.log(typeof this.props.children)
return (
<div>
{typeof this.props.children === 'object'
? React.cloneElement(this.props.children, { ref: (n) => this.form = n })
: null}
<button onClick={(e) => console.log(this.form.data)}>submit</button>
</div>
);
}
}
class Form extends React.Component {
constructor() {
super();
this.onChange = this.onChange.bind(this);
this.state = {};
}
onChange(e) {
this.data = e.target.value;
}
render() {
return (
<form>
<input type="text" onChange={this.onChange} />
</form>
);
}
}
ReactDOM.render(
<Card><Form /></Card>,
document.getElementById('container')
);
https://jsbin.com/fohehogozo/edit?js,console,output
By setting a property on the instance, you can then access that property from children by using a ref. I checked for typeof === object here, because there was only one child.
WARNING: this code is NOT PRODUCTION READY. Do not ever run this in production. The code I have demonstrated is a terrible hack, and you should never try this at home.
If you are trying to submit a form, maybe look at passing down an onChange event and storing the value (based on the name of the field) in the state of the Card. Then attach the onChange event on the inputs so as soon as they're updated, the data will be passed back up to the container for you to submit.
If you would like to split up the childrens passed, you can simply filter the children array to split up the children, however your childrens seem to be nested.
Why dont you let the cards children handle the separation between your inner container and other content?
I think restructuring in this case is more suitable than modifying the passed children property.
Also, pulling the submit button out of the actual form tags, would break your form as it would no longer submit without some custom connection between the button and the actual form.
Don't try to manipulate the DOM; it's generally an anti-pattern in React (though there are a few valid use cases). In your case, rather than literally trying to move the elements, I'd simply hide the button in the form and add it to the parent.
Assuming you have access to the internals of <LoginForm>, you can add a prop to hide the button:
const button =
<div class="flatbuttonWrapper">
<input type="submit"/>
</div>;
<Form>
<input type="text" name="email"/>
<input type="password" name="password"/>
{!this.props.hideButton && button}
</Form>
Add the button to the Card component:
render() {
return (
<div className="card">
<div className="inner">
{this.props.children != undefined ?
<div className="childrenWrapper">
{this.props.children}
</div>
: ""
}
</div>
<div class="flatbuttonWrapper">
<input type="submit"/>
</div>
</div>
);
}
Finally, in your parent:
<Card>
<LoginForm hideButton />
</Card>
All that said, it really feels like you need to structure your code better and break some of these components up into smaller, more reusable pieces. For example, the Card component probably shouldn't be affecting the button's style or conditionally rendering children; it should just add a frame around any children. Then you can create a more complex component that composes these simpler sub-components to to whatever you need.

Resources