Hi i want to create a dropDown in react with each item having an icon. As i tried react-select but its not showing the icon ,and also the selected value .When i remove value prop from react-select component than the label is showing. I want to create the dropdown like the this.
//USerInfo.js
import React from "react";
import { connect } from "react-redux";
import FontAwesome from "react-fontawesome";
import Select from "react-select";
import { setPresence } from "../../ducks/user";
import "./UserInfo.css";
class UserInfo extends React.Component {
// constructor(props) {
// super(props);
// this.state = {
// currentPresence: "available",
// };
// }
presenceOpts = [
{ value: "available", label: "Available" },
{ value: "dnd", label: "Busy" },
{ value: "away", label: "Away" },
];
setPresenceFun(presence) {
this.props.setPresence(presence);
}
renderPresenceOption(option) {
return (
<div className="presenceOption">
<FontAwesome name="circle" className={"presenceIcon " + option.value} />
{option.label}
</div>
);
}
renderPresenceValue(presence) {
return (
<div className="currentPresence">
<FontAwesome
name="circle"
className={"presenceIcon " + presence.value}
/>
</div>
);
}
render() {
return (
<div className="UserInfo">
{this.props.client.authenticated && (
<div className="authenticated">
<div className="user">
<div className="presenceControl">
<Select
name="presence"
value={this.props.user.presence.value}
options={this.presenceOpts}
onChange={this.setPresenceFun.bind(this)}
clearable={false}
optionRenderer={this.renderPresenceOption}
valueRenderer={this.renderPresenceValue}
/>
</div>
<div className="username">
<p>{this.props.client.jid.local}</p>
</div>
</div>
</div>
)}
</div>
);
}
}
const mapStateToProps = (state, props) => ({
client: state.client,
user: state.user,
});
const mapDispatchToProps = (dispatch, props) => {
return {
setPresence: (presence) => dispatch(setPresence(presence)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserInfo);
You can customize the option for dropdown
This may assist you in resolving the custom styling issue with react select.
https://codesandbox.io/s/react-select-add-custom-option-forked-u1iee?file=/src/index.js
Related
Hello peepz of the web,
I've ran into mysterious corridor, which's too dark for me to see what the hell I'm going.. would love someone's flashlight to shine the way.
I have created a simple, poor to do list program.
It's combined from Task.js, TaskList.js and NewTaskForm.js.
From the NewTaskForm.js I'm retrieving input, passing it to the parent, TaskList.js.
On TaskList.js I'm adding a key to the task object:
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
Now, in TaskList.js, in my render function, I'm creating Tasks:
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
now, what is so confusing for me is why when I'm doing in my Tasks.js class:
console.log(this.props.id);
it prints me an object?
I would expect it to.. print me a value? where along the way did it wrap it with an object?
Full (shitty) code below.
Plus #1, if anyone knows to tell me why still I get the warning the key, even though, at least for my poor experience, I have given it a key?
Plus #2, why even when I send the handleRemove function in TaskList.js the proper id, it doesn't erase the bloody task? :-(
Regards!
Task.js
import React, { Component } from 'react'
import './Task.css';
export default class Task extends Component {
constructor(props){
super(props);
this.handleRemove = this.handleRemove.bind(this);
}
handleRemove(evt){
evt.preventDefault();
console.log("MY ID MANNN");
console.log(this.props.id);
this.props.handleRemove(this.props.id.keyid);
console.log("CALLED");
}
render() {
return (
<div className='Task'>
<div className='Task-title'>
{this.props.taskTitle}
</div>
<div className='Task-text'>
{this.props.taskText}
</div>
<div>
<button className='Task-buttons'>Edit</button>
<button className='Task-buttons' onClick={this.handleRemove}>Delete</button>
</div>
</div>
)
}
}
NewTaskForm.js:
import React, { Component } from 'react';
import {v4} from 'uuid';
export default class NewTaskForm extends Component {
constructor(props){
super(props);
this.state = {
title: "", text: "", editing: false
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
// this.handleRemove = this.handleRemove.bind(this);
}
handleSubmit(evt){
evt.preventDefault();
// let stateWithID = { ...this.state, id: useId()};
this.props.handleSubmit(this.state);
this.setState({
title: "",
text: ""
})
}
handleChange(evt){
evt.preventDefault();
this.setState({
[evt.target.name]: evt.target.value
});
}
render() {
return (
<div>
<h2>Insert new Task:</h2>
<form onSubmit={this.handleSubmit}>
<label htmlFor="title">Title</label>
<input
name='title'
id='title'
type='text'
onChange={this.handleChange}
value={this.state.title}
/>
<div>
<label htmlFor="text">text</label>
<input
name='text'
id='text'
type='text'
onChange={this.handleChange}
value={this.state.text}
/>
</div>
<button>Submit</button>
</form>
</div>
)
}
}
TaskList.js
import React, { Component } from 'react'
import NewTaskForm from './NewTaskForm';
import Task from './Task';
import './TaskList.css';
import {v4} from 'uuid';
export default class TaskList extends Component {
constructor(props){
super(props);
this.state = {
todoArr: [
// {title: "test", text: "this shit", key: "", id: ""},
// {title: "test2", text: "this shi2", key: "", id: ""},
// {title: "test3", text: "this shi3", key: "", id: ""}
],
isEditing: false,
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
handleSubmit(task2add){
let keyid = v4();
console.log(keyid);
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
this.setState(st => ({
todoArr: [...st.todoArr, task2addWithID],
}));
}
handleRemove(keyid){
console.log("IN HANDLE REMOVE");
console.log(keyid);
this.setState(st => ({
todoArr: st.todoArr.filter(n => n.keyid !== keyid )
}));
}
handleEdit(id){
}
render() {
let tasklist = this.state.todoArr.map(task => (
<div>
<Task
taskTitle={task.title}
taskText={task.text}
key={task.key}
id={task.id}
handleRemove={this.handleRemove}
handleEdit={this.handleEdit}
/>
</div>
));
return (
<div>
<h1>TaskList</h1>
<div className='TaskList'>
<div className='TaskList-title'>
{tasklist}
</div>
<NewTaskForm key={1} handleSubmit={this.handleSubmit}/>
</div>
</div>
)
}
}
Because you have created an object here:
{ ...task2add, id: {keyid}, key: {keyid}};
{keyid}
is the same as
{keyid: keyid}
You wanted to do this:
{ ...task2add, id: keyid, key: keyid};
I'm assuming its because you're setting the state like this:
let task2addWithID = { ...task2add, id: {keyid}, key: {keyid}};
The id attribute is assigned an object. If you did:
let task2addWithID = { ...task2add, id: keyid, key: {keyid}};
console logging id should print out a string
I want to convert the functional component to class component.The code of modal is given below in office fabric class component.I want to convert in class component.I am new in the React and facing some difficulty while conversion functional to class component.
import * as React from 'react';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { hiddenContentStyle, mergeStyles } from 'office-ui-fabric-react/lib/Styling';
import { Toggle } from 'office-ui-fabric-react/lib/Toggle';
import { ContextualMenu } from 'office-ui-fabric-react/lib/ContextualMenu';
import { useId, useBoolean } from '#uifabric/react-hooks';
const dialogStyles = { main: { maxWidth: 450 } };
const dragOptions = {
moveMenuItemText: 'Move',
closeMenuItemText: 'Close',
menu: ContextualMenu,
};
const screenReaderOnly = mergeStyles(hiddenContentStyle);
const dialogContentProps = {
type: DialogType.normal,
title: 'Missing Subject',
closeButtonAriaLabel: 'Close',
subText: 'Do you want to send this message without a subject?',
};
export const DialogBasicExample: React.FunctionComponent = () => {
const [hideDialog, { toggle: toggleHideDialog }] = useBoolean(true);
const [isDraggable, { toggle: toggleIsDraggable }] = useBoolean(false);
const labelId: string = useId('dialogLabel');
const subTextId: string = useId('subTextLabel');
const modalProps = React.useMemo(
() => ({
titleAriaId: labelId,
subtitleAriaId: subTextId,
isBlocking: false,
styles: dialogStyles,
dragOptions: isDraggable ? dragOptions : undefined,
}),
[isDraggable],
);
return (
<>
<Toggle label="Is draggable" onChange={toggleIsDraggable} checked={isDraggable} />
<DefaultButton secondaryText="Opens the Sample Dialog" onClick={toggleHideDialog} text="Open Dialog" />
<label id={labelId} className={screenReaderOnly}>
My sample label
</label>
<label id={subTextId} className={screenReaderOnly}>
My sample description
</label>
<Dialog
hidden={hideDialog}
onDismiss={toggleHideDialog}
dialogContentProps={dialogContentProps}
modalProps={modalProps}
>
<DialogFooter>
<PrimaryButton onClick={toggleHideDialog} text="Send" />
<DefaultButton onClick={toggleHideDialog} text="Don't send" />
</DialogFooter>
</Dialog>
</>
);
};
class Services extends Component {
constructor(props) {
super(props);
this.state = {showoffer: false};
}
showOffers=( )=>{
this.setState({showoffer: !this.state.showoffer});
}
render() {
return (
<div className="OSServicesContainer">
<img className="OSlogomark" src={logomark} alt="logo mark" />
<article className="OssHeadingText">OOM INTERIORS OFFERS</article>
{offersdata.map((offers,index)=>{
return ( <div key={index} className="OssoffersContainermain">
<div className="OssoffersContainer">
<div className="OssofferHeadingmain">
<article className="OssofferHeading">{offers.heading}</article>
</div>
<article className="OssofferText">{offers.subheading}</article>
<div className="OssofferViewbtnmain">
<article key={index} className="OssofferViewbtn" onClick={this.showOffers}>{this.state.showoffer?"View Less":"View More"}</article>
</div>
</div>
{!this.state.showoffer?
null:
<div className="OssOfferSubCompmain">
{offers.offersub.map((offer,key) =>{
return <OssOfferSubComp ofrtext={offer.text} ofrsubtext={offer.subtext} />
})}
</div>}
</div>
)
})}
</div>);
}
}
export default Services;
Above is my code
i want to call showoffer function and update only that element clicked
please what shall i do it is triggering all elements
how to trigger single element??
You can try something like this:
`class Services extends Component {
constructor(props) {
super(props);
this.state = {showoffer: 0};
}
showOffers = ( offerIndex ) => {
this.setState({showoffer: offerIndex});
}
hideOffers = () => {
this.setState({showoffer: 0});
}
render() => {
...
<div className="OssofferViewbtnmain">
<article key={index} onClick={ () => this.showOffers(index) }>
{this.state.showoffer?"View Less":"View More"}
</article>
</div>
...
{
this.state.showOffer && this.state.showOffer === index
? // then show it
: ''
}
}`
Hey if you wish to have multiple items open at the same time you can do something like this where you mutate the mapped item to track show hide state. I have added a visible property to the list item that keeps track if the item is open or closed:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends Component {
state = {
items: [
{ header: "Test 1", extra: "Some extra content A" },
{ header: "Test 2", extra: "Some extra content B" },
{ header: "Test 3", extra: "Some extra content C" }
]
};
onItemClick(index) {
const selected = this.state.items[index];
this.setState({
items: [
...this.state.items.slice(0, index),
{ ...selected, visible: !selected.visible },
...this.state.items.slice(index + 1)
]
});
}
render() {
return (
<div>
<ul>
{this.state.items.map((item, index) => {
return (
<li
key={index}
style={{ cursor: "pointer" }}
onClick={() => this.onItemClick(index)}
>
<h3>{item.header}</h3>
{item.visible ? <div>{item.extra}</div> : null}
</li>
);
})}
</ul>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/busy-germain-hdmrn
I am still new in React and Redux. So, I know the existence of redux-form, but Im not intend to use in this project. So, what I am doing is creating a form without using redux-form. This form will grab the data from the reducers and pass it to backend API.
This is my main CreateListing.jsx page.
// #flow
import React from 'react';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { SEOService } from '[services]';
import CreateListingFormPage1 from './CreateListing/CreateListingFormPage1';
import CreateListingFormPage2 from './CreateListing/CreateListingFormPage2';
import CreateListingFormPage3 from './CreateListing/CreateListingFormPage3';
import WhereAmI from './CreateListing/WhereAmI';
import SuccessCreateListing from './CreateListing/SuccessCreateListing';
type Props = {
...props...
};
class CreateListing extends React.Component<Props> {
getPageBySequence(pagenum) {
// depending on whether User is logged in or not, show/hide the Login/Signup form which is Page3
let sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage3 ];
if (this.props.isLoggedIn) {
sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage2 ];
}
return sequence[pagenum-1];
}
getSubmitCreateListing = (e) => {
e.preventDefault();
const propertyType = this.props.listingType;
const propertyName = this.props.suggestedBuildings.selected;
const propertyBuildingType = this.props.propertyBuildingType;
const bedrooms = this.props.bed;
const bathrooms = this.props.bath;
const price = this.props.price;
const builtUp = this.props.builtUp;
const title = this.props.title;
const tenure = this.props.tenure;
const description = this.props.description;
/* IN CASE USER NOT YET LOGGGED IN */
if(this.props.isLoggedIn === false) {
const email = this.props.email;
const password = this.props.password;
this.props.cacheCreateListing({ email, password, propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
}
this.props.cacheCreateListing({ propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
if(CreateListingFormPage1.landedTypes.includes(propertyBuildingType)) {
this.props.geocodingRequired(true);
}
else {
this.props.geocodingRequired(false);
}
this.props.onSubmitCreateListing();
}
onAuthenticateAndCreateListingButton() {
if(this.props.isLoggedIn) {
return(
<div role="presentation">
<div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
<div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}>
<button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>
</div>
</div>
)
}
return <div className={`column ${this.props.formCurrentPage < 3 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
}
render() {
if(this.props.isListingCreated){
return <SuccessCreateListing />;
}
else if(this.props.isListingLoading){
return <div className="create-listing-spinner" />
}
const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
return (
<div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}>
<Helmet>
<title>{ SEOService.getMetaTitle('Create Property Listing') }</title>
{ SEOService.getCanonicalTag('/blogs') }
</Helmet>
<section className="CreateListing--Main">
<div className="CreateListing--Container">
<div className="CreateListing--WhereAmI">
<WhereAmI page={this.props.formCurrentPage} />
</div>
<div className="CreateListing--Body">
<form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }>
<CurrentPage />
<div className='columns'>
<div className='column'/>
{/* CHANGE THIS this.props.formCurrentPage < 3 later */}
<div className={`column ${this.props.formCurrentPage > 1 && this.props.formCurrentPage < 4 ? '':'displayNone'}`}>
<button type="button" className="Button button-back" onClick={this.props.onPrevClick}>
BACK
</button>
</div>
{ this.onAuthenticateAndCreateListingButton() }
<div className='column'/>
</div>
</form>
</div>
</div>
</section>
</div>
);
}
};
const MapStateToProps = (state: State) => ({...});
const MapDispatchToProps = (dispatch: Dispatch) => ({
onLoadingCreateListing: () => dispatch({type: 'CREATE_LISTING_LOADING'}),
onSubmitCreateListing: () => dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})
export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);
So, my <input type="text" /> are all from CreateListingFormPage1, CreateListingFormPage2 and CreateListingFormPage3 and put together in <CurrentPage />. My <form onSubmit={...}></form> is in this CreateListing.jsx page. Im not whether it is allowed to do it like this.
So, when I click submit, I got warning of Form submission canceled because the form is not connected.
My example of <input type="" /> in CreateListingFormPage1 are:
// #flow
import React from 'react';
import { connect } from 'react-redux';
import {Dropdown} from '[elements]';
type Props = {...props...};
class CreateListingFormPage2 extends React.Component<Props> {
static get selectTenure() { return ["Select Tenure"].concat(this.tenureTypes) };
static get selectTitle() { return ["Select Title"].concat(this.titleTypes) };
static get selectBedroom() { return["Select Bedrooms no"].concat(this.bedroomNo) };
static get selectBathroom() { return["Select Bathrooms no"].concat(this.bathroomNo) };
static get tenureTypes(){
return[
"FREEHOLD",
"LEASEHOLD",
"OTHERS"
]};
static get titleTypes(){
return[
"RESIDENTIAL",
"COMMERCIAL",
"INDUSTRIAL"
]};
static get bedroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
static get bathroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
get selectTenure() { return this.constructor.selectTenure; }
get selectTitle() { return this.constructor.selectTitle; }
get selectBedroom() { return this.constructor.selectBedroom; }
get selectBathroom() { return this.constructor.selectBathroom; }
get tenureTypes() { return this.constructor.tenureTypes; }
get titleTypes() { return this.constructor.titleTypes; }
get bedroomNo() { return this.constructor.bedroomNo; }
get bathroomNo() { return this.constructor.bathroomNo; }
hasInputError = (name) => {
if (this.props.errors[name]) {
return ' is-error';
}
return '';
}
render() {
return (
<div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'>
{/* <form className="Listing--form"> */}
<div className="Listing--bedrooms-bathrooms">
<div className="type-title">No. of Bedrooms</div>
<Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/>
<div className="type-title">Asking Price</div>
<input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) => this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/>
</div>
<div className="Listing--price-built-up">
<div className="type-title">No. of Bathrooms</div>
<Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/>
<div className="type-title">Built-up Size</div>
<input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) => this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/>
</div>
<div className="Listing--tenure">
<div className="type-tenure">Select Tenure</div>
<Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/>
</div>
<div className="Listing--title">
<div className="type-title">Select Title</div>
<Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/>
</div>
<div className="Listing--photos">
<div className="type-title">Upload Photos</div>
<button className={`text-input photos-input${ this.hasInputError('photos')}`}>Click to upload</button>
</div>
<div className="Listing--description">
<div className="type-title">Describe your property</div>
<textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) => this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/>
</div>
</div>
);
}
};
const MapStateToProps = (state: State) => ({
...
});
const MapDispatchToProps = (dispatch: Dispatch) => ({
...
})
export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);
Basically, there is nothing wrong with my redux store. All the value of the input is captured successfully. The problem is when submitting the form, either the onSubmit or my form structure method is incorrect.
This is CreateListing.js reducer should it be helpful:
const INITIAL_STATE= {
isListingLoading: false,
isListingCreated: false,
}
const CreateListing = (state = INITIAL_STATE, action) => {
switch(action.type){
case 'CREATE_LISTING_LOADING':
return Object.assign({}, state, {isListingLoading: true});
case 'CREATE_LISTING_SUBMIT_FORM':
return Object.assign({}, state, {isListingCreated: true});
default:
return state;
} }
export default CreateListing;
Any help is much appreciated.
if you have any other buttons in your form you should add type="button".
so make changes like this.
<button type="button" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>
Using the example of initializingFromState within Redux-Form, I am trying to set this up dynamically. This is to edit a particular book in a list of books, and is using a simple api set up in express.js.
The full container is below. I somehow need to pass in initialValues, within the mapStateToProps function. In the example, it is done via a static object, but I can't work out how to use the information I have pulled in via fetchBook, and pass it to initialValues.
Container:
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { fetchBook, editBook } from '../actions/index';
class BookEdit extends Component {
componentWillMount() {
this.props.fetchBook(this.props.params.id);
}
static contextTypes = {
router: PropTypes.object
}
onSubmit(props) {
this.props.editBook(this.props.book.id, props)
.then(() => {
this.context.router.push('/');
});
}
const data = {
title: {this.props.book.title},
description: {this.props.author}
}
render() {
const { fields: { title, author }, handleSubmit } = this.props;
const { book } = this.props;
if (!book) {
return (
<div>
<p>Loading...</p>
</div>
)
}
return (
<div>
<Link to="/">Back</Link>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<h2>Add a new book</h2>
<label>Title</label>
<input type="text" {...title} />
<div className="text-help">{title.touched ? title.error : ''}</div>
<label>Author</label>
<input type="text" {...author} />
<div className="text-help">{author.touched ? author.error : ''}</div>
<button type="submit">Add</button>
<Link to="/" className="button">Go back</Link>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
book: state.books.book,
initialValues: // how do I pass in the books here?
};
}
export default reduxForm({
form: 'EditBookForm',
fields: ['title', 'author']
}, mapStateToProps, { fetchBook, editBook })(BookEdit);
Thank you.
Your form values aren't what's in state.books.book? I think this is all you're looking for:
function mapStateToProps(state) {
return {
book: state.books.book,
initialValues: state.books.book
};
}
Since you're only really looking at this.props.book to know if it's loaded or not, it might be more explicit to do something like:
function mapStateToProps(state) {
return {
loaded: !!state.books.book,
initialValues: state.books.book
};
}
Hope that helps.
In related to above question, Erik. I have following form and not sure why it is not Validating on submit. It loads the data into fields but when I hit submit the validation fails.
Form_Bayan.js
import React, {Component, PropTypes} from "react";
import {browserHistory} from "react-router";
import {reduxForm, Field} from "redux-form";
import {MyCustomInput, MySimpleInput, MyCustomSelect} from "./__form_field_components";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import {
ADMIN_FETCH_AUTOSUGGESTS_Lbl,
adminFetchAutoSuggestCats_act,
ADMIN_GENERATESLUG_Lbl,
adminGenerateSlug_act,
ADMIN_GETCATID_BYNAME_Lbl,
adminGetCatIdByName_act,
ADMIN_ADDNEWBAYAAN_Lbl,
adminAddNewBayaan_act,
adminFetchArticlesByCat_act,
adminUpdateBayaan_act
} from "../../actions/adminActionCreators";
import _ from "lodash";
class NewBayanForm extends Component {
constructor(props) {
super(props); // this component inherits "toggleViewFunction" function through props for redirection
this.generateSlug = this.generateSlug.bind(this);
this.state = {
submitButtonMeta: {
btnTitle: "Save",
btnClass: "btn btn-default",
btnIcon: null,
disabled: false
},
globalMessage: { // set when an action is performed by ActionCreation+Reducer and a message is returned
message: "",
className: ""
},
tempData: {
//the_bayaansMainCat_id : 1, // '1' refers to the 'Bayaans' parent category in admin , this ID is used here for different sort of lookups i.e. fetch available subcats for autosuggest, fetch cat ID by name under parent catID
the_bayaansMainCat_id: this.props.associatedMainCatId, // being passed from parent component to avoide redundent declaration
the_autoSuggestCatList: [],
slug: "",
the_catId: null
}
};
}
resetMessageState() {
var noMsg = {message: "", className: ""};
this.setState({globalMessage: noMsg});
}
componentDidMount() {
console.log("<NewBayanForm> (componentDidMount)");
this.props.adminFetchAutoSuggestCats_act(this.state.tempData.the_bayaansMainCat_id);
}
doSubmit(props) {
//console.log("----- submitting form -----");
//console.log(props);
this.disableSubmitButton();
// prepare data for submit request
// item_title, item_slug, content, picture, attachment, media_path, reference, tag_keywords, author_name, cat_id, date_created
var newBayanObj = {
item_title: props.titleTxt,
item_slug: this.state.tempData.slug,
content: props.videoIdTxt,
picture: "",
attachment: "",
media_path: "https://www.youtube.com/watch?v=" + props.videoIdTxt,
reference: "",
tag_keywords: props.keywordsTxt,
author_name: props.authorTxt,
cat_id: this.state.tempData.the_catId
};
this.props.adminUpdateBayaan_act(newBayaanObj)
.then(() => {
console.log("%c <NewBayanForm> (doSubmit) Updated bayaan, refetching updated bayaans list...", "color:blue;font-weight:bold;");
this.props.adminFetchArticlesByCat_act(this.props.associatedMainCatId)
.then(() => {
console.log("%c <NewBayanForm> (doSubmit) Redirecting to Gallery after update...", "color:blue;font-weight:bold;");
this.props.toggleViewFunction(); // comming from Parent Class (bayaansPage)
});
});
}
disableSubmitButton() {
console.log("<NewBayanForm> (disableSubmitButton)");
// Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
var newButtonState = {
btnTitle: "Please wait... ",
btnClass: "btn btn-disabled",
btnIcon: null,
disabled: true
};
this.setState({submitButtonMeta: newButtonState});
this.resetMessageState(); // Need to reset message state when retrying for form submit after 1st failure
}
enableSubmitButton() {
console.log("<NewBayanForm> (enableSubmitButton)");
// Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
var newButtonState = {btnTitle: "Save", btnClass: "btn btn-default", btnIcon: null, disabled: false};
this.setState({submitButtonMeta: newButtonState});
}
fetchCategoryId(value) {
console.log('<NewBayanForm> (fetchCategoryId) input-Value:', value); // make API call to fetch / generate category ID for this post
this.props.adminGetCatIdByName_act(value, this.state.tempData.the_bayaansMainCat_id); // '1': refers to look up under 'Bayaans' parent category for the specified category name
}
// will always receive and triggers when there are 'new props' and not old/same props
componentWillReceiveProps(nextProps) { // required when props are passed/changed from parent source. And we want to do some operation as props are changed (Ref: http://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form)
console.log("<NewBayanForm> (componentWillReceiveProps) nextProps: ", nextProps); // OK
//console.log("this.props : ", this.props); // OK
//console.log("nextProps.siteEssentials.actionsResult : ", nextProps.siteEssentials.actionsResult); // OK
if (nextProps.hasOwnProperty("siteEssentials")) { // if action status appeared as Done!
if (nextProps.siteEssentials.hasOwnProperty("actionsResult")) { // if action status appeared as Done!
if (nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] !== "FAILED") {
var clonedState = this.state.tempData;
clonedState.the_catId = nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl];
//var newTempState = {slug: this.state.tempData.slug, the_catId: nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] };
this.setState({tempData: clonedState});
}
if (nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl] !== "FAILED") {
var clonedState = this.state.tempData;
clonedState.the_autoSuggestCatList = nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl];
this.setState({tempData: clonedState});
}
console.log("<NewBayanForm> (componentWillReceiveProps) new-State:", this.state);
}
}
}
render() { // rendering Edit form
const {handleSubmit} = this.props;
console.log('<NewBayanForm> (render_editForm) this.props:', this.props);
return (
<div className="adminForm">
<form onSubmit={handleSubmit(this.doSubmit.bind(this))}>
<div className="col-sm-6">
<div className="row">
<div className="col-sm-5"><label>Title:</label></div>
<div className="col-sm-7"><Field name="titleTxt" component={MySimpleInput}
defaultValue={this.props.name} type="text"
placeholder="Enter Title"/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Slug:</label></div>
<div className="col-sm-7">{this.state.tempData.slug || this.props.slug} <input
type="hidden" name="slugTxt" value={this.state.tempData.slug}/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Select Category:</label></div>
<div className="col-sm-7"><Field name="catTxt" component={MyCustomSelect}
defaultValue={this.props.category_name} type="text"
placeholder="Select or Type a New"
selectableOptionsList={this.state.tempData.the_autoSuggestCatList}
onSelectionDone={ this.fetchCategoryId.bind(this) }/>
<input type="hidden" name="catIdTxt"
value={this.state.tempData.the_catId || this.props.category_id}/>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="row">
<div className="col-sm-5"><label>Youtube Video ID:</label></div>
<div className="col-sm-7"><Field name="videoIdTxt" component={MySimpleInput}
defaultValue={this.props.content} type="text"
placeholder="TsQs9aDKwrw"/></div>
<div className="col-sm-12 hint"><b>Hint: </b> https://www.youtube.com/watch?v=<span
className="highlight">TsQs9aDKwrw</span></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Author/Speaker:</label></div>
<div className="col-sm-7"><Field name="authorTxt" component={MySimpleInput}
defaultValue={this.props.author} type="text"/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Tags/Keywords:</label></div>
<div className="col-sm-7"><Field name="keywordsTxt" component={MySimpleInput}
defaultValue={this.props.tag_keywords} type="text"/>
</div>
</div>
</div>
<div className="row">
<div className={this.state.globalMessage.className}>{this.state.globalMessage.message}</div>
</div>
<div className="buttonControls">
<a className="cancelBtn" onClick={this.props.toggleViewFunction}>Cancel</a>
<button className={this.state.submitButtonMeta.btnClass}
disabled={this.state.submitButtonMeta.disabled}>
{this.state.submitButtonMeta.btnTitle}</button>
</div>
</form>
</div>
);
}
}
function validate(values) { // Validate function being called on Blur
const errors = {};
if (!values.titleTxt)
errors.titleTxt = "Enter Title";
if (!values.catTxt)
errors.catTxt = "Select/Enter a Category";
if (!values.videoIdTxt)
errors.videoIdTxt = "Enter youtube video ID (follow the provided hint)";
if (!values.keywordsTxt)
errors.keywordsTxt = "Enter keywords (will help in search)";
return errors;
}
// ReduxForm decorator
const newBayanFormAdmin_reduxformObj = reduxForm({
form: "newBayanFormAdmin", // any unique name of our form
validate // totally equivelent to--> validate: validate
});
function mapStateToProps({siteEssentials}, ownProps) {
console.log("<NewBayanForm> (mapStateToProps) siteEssentials:", siteEssentials);
// 1st param is related to our Redux State, 2nd param relates to our own component props
var initialValues = {
titleTxt: ownProps.name,
slugTxt: ownProps.slug,
catTxt: ownProps.category_name,
catIdTxt: ownProps.category_id,
videoIdTxt: ownProps.content,
authorTxt: ownProps.author,
keywordsTxt: ownProps.tag_keywords
};
console.log("<NewBayanForm> (mapStateToProps) initialValues: ", initialValues);
return ({siteEssentials}, initialValues);
};
function mapDispatchToProps(dispatch) {
return bindActionCreators({
adminFetchAutoSuggestCats_act,
adminGenerateSlug_act,
adminGetCatIdByName_act,
adminAddNewBayaan_act,
adminFetchArticlesByCat_act
}, dispatch);
};
NewBayanForm = connect(mapStateToProps, mapDispatchToProps) (newBayanFormAdmin_reduxformObj(NewBayanForm));
export default NewBayanForm;