I'm following the example here: https://redux-form.com/6.6.3/examples/wizard/
I've managed to make everything I needed up until now, but I'm getting stuck on how to make custom radio buttons.
This is what I have so far, but I can't get the radio button to change when clicking on the divs that represent it
import React, { Component } from 'react'
import './pictureCard.css'
import '../../../../app.css'
import MaterialIcon from 'react-google-material-icons'
import { Field } from 'redux-form'
export default class PictureCard extends Component {
constructor(props) {
super(props)
this.state = {
isChecked: false
}
}
handleTick = event => {
const card = event.target
card.classList.toggle('question__checkbox--selected')
this.setState(prevState => {
return { isChecked: !prevState.isChecked }
})
}
render() {
const {
tabOrder,
cardName,
cardKey,
cardLabel,
thumbnail,
thumbnailAlt
} = this.props
return (
<li
className="question__choice"
tabIndex={tabOrder}
onClick={this.handleTick}
>
<Field
name={cardName}
type="radio"
component="input"
value={cardLabel}
checked={this.state.isChecked}
/>
<div className="question__tick-wrap">
<MaterialIcon icon="check" />
</div>
{thumbnail === undefined ? null : (
<div className="question__image-wrap">
<img src={thumbnail} alt={thumbnailAlt} />
</div>
)}
<div className="question__text-wrap flex flex--center-vertical">
<div className="question__label">
<div className="question__letter">
<span>{cardKey}</span>
</div>
</div>
<div className="question__text-label">{cardLabel}</div>
</div>
<div className="question__bg" />
</li>
)
}
}
I can programatically change the value, but it doesn't update in the store, and I don't know why :(
This is what I tried:
import React, { Component } from 'react'
import './pictureCard.css'
import '../../../../app.css'
import MaterialIcon from 'react-google-material-icons'
import { Field } from 'redux-form'
export default class PictureCard extends Component {
constructor(props) {
super(props)
this.state = {
valueTest: false
}
}
handleTick = event => {
const card = event.target
card.classList.toggle('question__checkbox--selected')
this.setState({ valueTest: 'Hello' })
}
render() {
const {
input,
tabOrder,
cardName,
cardKey,
cardLabel,
thumbnail,
thumbnailAlt
} = this.props
return (
<li
className="question__choice"
tabIndex={tabOrder}
onClick={this.handleTick}
>
<input
{...input}
name={cardName}
value={this.state.valueTest}
type="text"
tabIndex={tabOrder}
/>
<div className="question__tick-wrap">
<MaterialIcon icon="check" />
</div>
{thumbnail === undefined ? null : (
<div className="question__image-wrap">
<img src={thumbnail} alt={thumbnailAlt} />
</div>
)}
<div className="question__text-wrap flex flex--center-vertical">
<div className="question__label">
<div className="question__letter">
<span>{cardKey}</span>
</div>
</div>
<div className="question__text-label">{cardLabel}</div>
</div>
<div className="question__bg" />
</li>
)
}
}
Related
I am trying to render a Button in React.js that would pop up a modal which reads "Submit Comment". However, the page turns up blank when i add the CommentForm component inside the RenderComments function. It works fine when I add a HTML component like "p" but doesnt work for CommentForm and "Button". Please help. I'm new to React.
import React, {Component} from "react";
import { Link } from "react-router-dom";
import { Modal, ModalHeader, ModalBody } from "bootstrap-react";
import { Button } from 'react';
import { Card, CardImg, CardText, CardBody, CardTitle, Breadcrumb,
BreadcrumbItem } from "reactstrap";
class CommentForm extends Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isModalOpen: false
};
}
toggleModal() {
this.setState({
isModalOpen: !this.state.isModalOpen
})
}
handleSubmitComment(values) {
}
render() {
return (
<div>
<Button outline onClick={this.toggleModal}>
<span className="fa fa-pen fa-lg">Submit Comment</span>
</Button>
<Modal isOpen={this.state.isModalOpen} toggle={this.toggleModal}>
<ModalHeader toggle={this.toggleModal}>Submit Comment</ModalHeader>
<ModalBody>
</ModalBody>
</Modal>
</div>
);
}
}
function RenderDish({dish}) {
if (dish != null) {
return (
<div className='col-12 col-md-5 m-1'>
<Card>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle> {dish.name}</CardTitle>
<CardText> {dish.description} </CardText>
</CardBody>
</Card>
</div>
);
}
else {
return (
<div></div>
);
}
}
function RenderComments({comments}){
if (comments != null)
return (
<div className='col-12 col-md-5 m-1'>
<h4> Comments </h4>
<ul className='list-unstyled'>
{comments.map(comment => {
return (
<li key={comment.id}>
<p>{comment.comment}</p>
<p>-- {comment.author},
{new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: '2-digit'
}).format(new Date(comment.date))}
</p>
</li>
);
})}
</ul>
<CommentForm />
</div>
);
else
return ( <div></div>);
}
const DishDetail = (props) => {
console.log('DishDetail Component render is invoked')
console.log(props.dish);
console.log(props.comments);
if (props.dish != null)
return (
<div className="container">
<div className='row'>
<Breadcrumb>
<BreadcrumbItem><Link to='/menu'>Menu</Link></BreadcrumbItem>
<BreadcrumbItem active>{props.dish.name}</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{props.dish.name}</h3>
<hr />
</div>
</div>
<div className="row">
<RenderDish dish={props.dish} />
<RenderComments comments={props.comments} />
</div>
</div>
);
else
return(
<div></div>
);
}
export default DishDetail;
I do not believe that React exposes a Button component, which you seem to try to use in import { Button } from 'react';
Should that Button be coming from the reactstrap package as well ?
instead of this :
import { Button } from 'react';
You should use
import { Button } from 'react-bootstrap'
or
import { Button } from 'reactstrap' pick which library is your prefer.
Also I don't think bootstrap-react is true (also there is one but not used commonly). for this line of your code import { Modal, ModalHeader, ModalBody } from "bootstrap-react"; I belive it should be import { Modal, ModalHeader, ModalBody } from "reactstrap"; because all these u called(Modal, ModalHeader, ModalBody) perfectly match with reactstrap.
In addition to #FaizErturk's answer, in toggleModal the callback argument of setState should be used:
this.setState((state)=>({
isModalOpen: !state.isModalOpen
}));
This prevents stale state values from being used to update.
See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
usually in a blog website user can comment with text or an image and so on ,and in my case when user comment with an image that have a dimension larger than the container the view affected as below :
and actually i'am storing the images in Firebase storage and i can't control those images .
this is the code of this page
Article.js :
import React from 'react';
import './Articles.css' ;
import {Link } from 'react-router-dom';
import ImageUploader from 'react-images-upload';
import { Editor } from '#tinymce/tinymce-react';
import {storage} from '../firebase/firebase';
class Articles extends React.Component{
constructor(props){
super(props);
this.state={
title:this.props.location.title,
comment:'',
postid:this.props.location.postid,
arraycomments:this.props.location.arraycomments,
postcontent:this.props.location.postcontent,
image:null,
url:'',
progress:0
}
this.handleChange = this.handleChange.bind(this)
this.handleChange = this.handleChange.bind(this)
}
onchangecomment=(event)=>{
this.setState({comment:event.target.value});
console.log(this.state.comment)
}
handleChange(event) {
if(event.target.files[0]){
const image=event.target.files[0];
this.setState(()=>({image}));
console.log(this.state.image)
}
}
handleUpload=()=>{
const {image}=this.state;
const uploadTask=storage.ref(`images/${image.name}`).put(image);
if(image){
document.getElementById('progress').style.display="block";
}
uploadTask.on('state_changed',
(snapshot)=>{
const progress=Math.round((snapshot.bytesTransferred/snapshot.totalBytes)*100);
this.setState({progress})
}
,(error)=>{
console.log(error)
},
()=>{
storage.ref('images').child(image.name).getDownloadURL().then(url=>{
this.setState({url})
document.getElementById('txtarea').value +=`<img src=${this.state.url} alt='image'/>`;
document.getElementById('placehoder').style.display="block";
document.getElementById('progress').style.display="none";
})
})
}
addcomment=()=>{
fetch('http://localhost:3002/Addcomment',{
method:'post',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
comment:this.state.comment,
postid:this.state.postid
})
}).then(resposne=>{})
.catch(err=>{console.log('not added from the quesiotns')})
document.getElementById('txtarea').value='';
}
render()
{
const {postcontent}=this.state;
return(
<div className='article'>
<div dangerouslySetInnerHTML={{ __html:postcontent }} />
<h4>Discussion</h4>
<div className='submitcommentform'>
<textarea id='txtarea' placeholder='Add discussion'
onChange={this.onchangecomment}
style={{height:'127px', width:'687px',padding:'6px 6px'}}>
</textarea>
<div className='submit-wrapper-actions'>
<Link to='' ><img className='wrapperimgage' src='https://practicaldev-herokuapp-com.freetls.fastly.net/assets/info-b2ce6a88ddd367e1416cd4c05aab2edee2d0b2c355d7b2aae1821eec48014e11.svg' height='21px'/></Link>
<label for="choosefile" class="btnimage">Select Image</label>
<input id='choosefile'type='file' onChange={this.handleChange} style={{visibility:'hidden'}} />
<button id='btnuplod' onClick={this.handleUpload}>Upload Image</button>
<button id='btnsubmit'onClick={this.addcomment}>Submit</button>
</div>
<div className='placeh-progr'>
<img id='placehoder' alt='uploaded image' src={this.state.url || "https://via.placeholder.com/150"} width='150px' height='150px'
style={{display:'none'}}
/>
<progress id='progress' value={this.state.progress} max="100" style={{display:'none'}} />
</div>
</div>
<div>
{
this.state.arraycomments.map((post,i)=>{
return (
<div className='commentsection' key={i} dangerouslySetInnerHTML={{ __html:post.comm_content }} />
);
})
}
</div>
</div>
);
}
}
export default Articles;
My question : is there any way to make the image smaller than the container.
Why not use CSS for that?
.your-image-class,
.container-class img {
max-width: 100%;
}
Either put a class for an image or do it "globally" for images in a container class.
I have a set of items that needs to be shown in the UI, like a header and list of items under it. There is a parent component where I am passing this data to a file that is shown below. Based on this the parent-child layout is shown. Now I need to expand/collapse based on the click of the header.
There is a class "open" and "close " that can be attached to the div. Based on it the it gets collapse/expands. The point is how do it item wise
Can someone help
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className="an-panel expand-panel expand-close">
<div className="an-panel-header">
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<Child
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
Ok let me explain it to you, here is your code
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
navBarStatus: false,
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
changeNavBar = (e, status)=>{
this.setState({navBarStatus: !status});
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className={`an-panel expand-panel ${this.state.navBarStatus ? "expand-open" : "expand-close"}`}>
<div className="an-panel-header" onClick={(e)=>this.changeNavBar(e, this.state.navBarStatus)}>
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<ChildSetting
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
You can see I have taken a new property in state navBarStatus. Based on navBarStatus value I am changing CSS class which will expand/close your attached div
I have a userlist which contains name and email id of each user. I want to display it using the .map() method on userlist state variable. I have created displayusers() function to display the users but I am getting failed to compile error.
Code:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
userlist:[
{'name':'Rohan Singh',
'email':'rohan#gmail.com'
},
{'name':'Mohan Singh',
'email':'mohan#gmail.com'
},
{'name':'Rakesh Roy',
'email':'rakesh#gmail.com'
},
{'name':'Sunil Shah',
'email':'sunil#gmail.com'
}]
}
}
displayusers(){
return this.state.userlist.map( user => {
return(
<div className="item-card">
<div className="sub">
<div className="type">Username: {user.name}</div>
<div className="members">Email: {user.email}</div>
</div>
<div className="del-wrap">
<img src={require("../../images/cancel.svg")}/>
</div>
</div>
);
})
}
render() {
return(
<div className="users-wrap">
<h1>Users</h1>
<div className="task-content">
<div className="user-wrap">
<div className="users">
{this.displayusers()}
</div>
</div>
</div>
</div>
);
}
}
export default App;
I think you forgot about adding a key attribute to the element and there's missing </div> closing tag in your map function.
See the corrected code:
displayusers(){
return this.state.userlist.map( user => {
return(
<div className="item-card" key={user.name}>
<div className="sub">
<div className="type">Username: {user.name}</div>
<div className="members">Email: {user.email}</div>
</div>
<div className="del-wrap">
<img src={require("../../images/cancel.svg")}/>
</div>
</div>
);
});
}
You need to bind your displayusers function to this. You can do that in the constructor.
Update your code as following:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
userlist:[
{'name':'Rohan Singh',
'email':'rohan#gmail.com'
},
{'name':'Mohan Singh',
'email':'mohan#gmail.com'
},
{'name':'Rakesh Roy',
'email':'rakesh#gmail.com'
},
{'name':'Sunil Shah',
'email':'sunil#gmail.com'
}]
};
this.displayusers = this.displayusers.bind(this); // you need to add this line
}
displayusers(){
return this.state.userlist.map((user, index) => {
return(
<div className="item-card" key={index}>
<div className="sub">
<div className="type">Username: {user.name}</div>
<div className="members">Email: {user.email}</div>
</div>
<div className="del-wrap">
<img src={require("../../images/cancel.svg")}/>
</div>
);
})
}
render() {
return(
<div className="users-wrap">
<h1>Users</h1>
<div className="task-content">
<div className="user-wrap">
<div className="users">
{this.displayusers()}
</div>
</div>
</div>
</div>
);
}
}
export default App;
I have 3 React components:
-Home - need to display the component here when image is
clicked from the Header component
-Header- Contains the image tag that will be clicked to
show the AllSites Component.
-AllSites - component that needs displayed in Home component when Image is
clicked in the Header component.
Header
export class Header extends React.Component<any, any> {
private readonly searchServerUrl = "";
private appButtonElement: HTMLElement;
constructor(props: any) {
super(props);
this.state = { showAppMenu: false };
}
render() {
const { showAppMenu } = this.state;
const { className, navItems, singleColumn, appItems } = this.props;
return (
<header className={className}>
<div className="app-icon">
<button className="nav-button" onClick={() => this.toggleAppMenu()} ref={(menuButton: any) => this.appButtonElement = menuButton}><i className="ms-Icon ms-Icon--Waffle" aria-hidden="true"></i></button>
</div>
***When image is clicked, show the <AllSites/> component in the HomeComponent below.***
<img src="/Styles/Images/logo/loop-logo-white.png" className="nav-logo" onClick={} />
{showAppMenu ? <ApplicationMenu navItems={appItems} targetElement={this.appButtonElement} onDismiss={() => this.onDismiss()} /> : null}
<div className="nav-container"><TopNavigation classNam
e={className} navItems={navItems} singleColumn={singleColumn} /></div>
<div className="search-container">
<SearchBox onSearch={(searchTerm: string) => this.executeSearch(searchTerm)} />
</div>
</header>
);
}
Home
export class HomeComponent extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = { navItems: [], appItems: [], singleColumnLayout: false, showAllSites: false };
}
componentDidMount() {
this.checkWidth();
window.addEventListener("resize", this.checkWidth.bind(this));
this.fetchNavigation()
.then(nav => this.setState({ navItems: nav }));
this.fetchAppIcons()
.then(icons => this.setState({ appItems: icons }));
}
componentWillUnmount(): void {
window.addEventListener("resize", this.checkWidth.bind(this));
}
render() {
const { navItems, appItems, singleColumnLayout } = this.state;
return (
<Fabric>
<Header navItems={navItems} appItems={appItems} singleColumn={singleColumnLayout} />
<div className="main-container">
<AlertBar />
<div className="main-content">
<div className="ms-Grid">
When the image tag is clicked, I need to render the <AllSites/> component here
<Hero singleColumn={singleColumnLayout} />
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 ms-xl4 webpart-container">
<UpcomingEvents />
</div>
<div className="ms-Grid-col ms-sm12 ms-xl4 webpart-container">
<EmployeeNews />
</div>
<div className="ms-Grid-col ms-sm12 ms-xl4 webpart-container">
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-sm12 webpart-container">
<IndustryNews />
</div>
<div className="ms-Grid-col ms-sm12 webpart-container">
<Quote />
</div>
</div>
</div>
</div>
</div>
<Footer navItems={navItems} />
</div>
</div>
</Fabric>
);
}
In the simplest approach you will need a common parent component for your Home and Header that will hold some shared state for them and that will pass a callback to update this state as a prop to Header. In the shared state you need a flag that will be responsible for showing/hiding AllSites component, this flag you will pass as a prop to Home.
You can see a basic example here
If you need a more advanced state management solution, you can check redux library