I have a react code
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import SelectBox from '../SelectBox';
import Icon from '../Icon';
import Input from '../Input';
import CountryItem from './components/CountryItem';
const PhoneNumber = props => {
const { children, className } = props;
const [selectedOption, setSelectedOption] = useState('');
const [inputParam, setInputParam] = useState('');
const inputParamChangeHandler = event => {
const inputChar = event.target.value;
setInputParam(inputChar);
console.log({ inputChar });
};
const selectedOptionChangeHandler = event => {
const valueSelected = event.target.value;
setSelectedOption(valueSelected);
console.log({ valueSelected });
};
console.log({ children });
const childrenWithPropsFromParent = React.Children.map(children, child => child);
const optionsWithImage = [
{
value: '+880',
label: <CountryItem />,
},
{
value: '+55',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Brazil</span>
<span className="phone-number__country-code">(+55)</span>
</span>
</span>
),
},
{
value: '+40',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Romania</span>
<span className="phone-number__country-code">(+40)</span>
</span>
</span>
),
},
{
value: '+44',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">United Kingdom</span>
<span className="phone-number__country-code">(+44)</span>
</span>
</span>
),
},
{
value: '+1',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Bahamas</span>
<span className="phone-number__country-code">(+1)</span>
</span>
</span>
),
},
];
return (
<div className={classNames('phone-number', className)}>
<div>Phone Number</div>
<div className="phone-number__wrapper">
<SelectBox
size="small"
value={selectedOption}
touched={false}
onChange={selectedOptionChangeHandler}
isSearchable={false}
isDisabled={false}
isClearable={false}
options={optionsWithImage}
width="small"
/>
<Input value={inputParam} onChange={inputParamChangeHandler} placeholder="XXXX XXX XXX" />
</div>
{/* {childrenWithPropsFromParent} */}
</div>
);
};
PhoneNumber.defaultProps = {
children: null,
className: null,
};
PhoneNumber.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
};
export default PhoneNumber;
SelectBox Component:
import React, { Component } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import Select from 'react-select';
import CreatableSelect from 'react-select/creatable';
import './select-box.styles.scss';
import colorOptions from '../../helpers/colorOptions';
import Tooltip from '../Tooltip';
import Icon from '../Icon';
const sizeBasedSelector = (element, size) => {
switch (size) {
case 'tiny':
return `select-box__${element}--tiny`;
case 'small':
return `select-box__${element}--small`;
case 'large':
return `select-box__${element}--large`;
default:
return null;
}
};
class SelectBox extends Component {
constructor(props) {
super(props);
this.state = {
selectedOption: props.value,
};
}
customTheme = theme => {
const { primary, primary75, primary50, primary25 } = this.props.colors;
return {
...theme,
colors: {
...theme.colors,
primary,
primary75,
primary50,
primary25,
},
};
};
renderPlaceHolder = () => {
const { preIcon, placeholderText, size } = this.props;
return (
<div className="select-box__placeholder">
{preIcon && (
<span className={classnames('select-box__pre-icon', sizeBasedSelector('pre-icon', size))}>
{preIcon}
</span>
)}
<span
className={classnames(
'select-box__placeholder-text',
sizeBasedSelector('placeholder-text', size)
)}
>
{placeholderText}
</span>
</div>
);
};
handleChange = selectedOption => {
this.setState({ selectedOption });
this.props.onChange(selectedOption);
};
handleInputChange = inputValue => {
this.props.onInputChange(inputValue);
};
getFlags = () => {
const { isClearable, isDisabled, isMulti, isSearchable } = this.props;
return {
isClearable,
isDisabled,
isMulti,
isSearchable,
};
};
render() {
const { selectedOption } = this.state;
const {
autoFocus,
className,
classNamePrefix,
colors,
errorMsg,
label,
name,
options,
size,
touched,
isMulti,
isDisabled,
isCreatable,
width,
required,
} = this.props;
const hasError = touched && errorMsg;
const selectProps = {
...this.getFlags(),
autoFocus,
className: classnames(
`${className} ${className}--${size}`,
sizeBasedSelector('width', width),
{
'select-box-container--notMulti': !isMulti,
'select-box-container--error': hasError,
}
),
classNamePrefix,
colors,
theme: this.customTheme,
value: selectedOption,
name,
options,
onInputChange: this.handleInputChange,
onChange: this.handleChange,
placeholder: this.renderPlaceHolder(),
required,
};
return (
<>
{label && (
<span
className={classnames(`select-box__label select-box__label--${size}`, {
'select-box__label--required': required,
'select-box__label--disabled': isDisabled,
})}
>
{label}
</span>
)}
<div className="select-box-wrapper">
{isCreatable ? <CreatableSelect {...selectProps} /> : <Select {...selectProps} />}
{errorMsg && (
<Tooltip
classNameForWrapper="select-box__tooltip-wrapper"
classNameForTooltip="select-box__tooltip"
content={errorMsg}
position="bottom-right"
gap={0}
>
<Icon name="invalid" />
</Tooltip>
)}
</div>
</>
);
}
}
SelectBox.defaultProps = {
autoFocus: false,
className: 'select-box-container',
classNamePrefix: 'select-box',
colors: {
primary: colorOptions.coolGray20,
primary75: colorOptions.coolGray45,
primary50: colorOptions.coolGray70,
primary25: colorOptions.coolGray95,
},
errorMsg: '',
isClearable: true,
isCreatable: false,
isDisabled: false,
isMulti: false,
isSearchable: true,
label: '',
name: 'select-box',
onChange: () => {},
onInputChange: () => {},
options: [],
placeholderText: 'Select...',
preIcon: null,
required: false,
size: 'small',
touched: false,
value: null,
width: 'small',
};
SelectBox.propTypes = {
autoFocus: PropTypes.bool,
className: PropTypes.string,
classNamePrefix: PropTypes.string,
colors: PropTypes.shape({
primary: PropTypes.string,
primary75: PropTypes.string,
primary50: PropTypes.string,
primary25: PropTypes.string,
}),
errorMsg: PropTypes.string,
isClearable: PropTypes.bool,
isCreatable: PropTypes.bool,
isDisabled: PropTypes.bool,
isMulti: PropTypes.bool,
isSearchable: PropTypes.bool,
label: PropTypes.string,
name: PropTypes.string,
onChange: PropTypes.func,
onInputChange: PropTypes.func,
options: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.string,
})
),
preIcon: PropTypes.node,
placeholderText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
value: PropTypes.oneOf([
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})
),
]),
required: PropTypes.bool,
size: PropTypes.oneOf(['tiny', 'small', 'large']),
touched: PropTypes.bool,
width: PropTypes.oneOf(['tiny', 'small', 'large', 'full']),
};
export default SelectBox;
now I want to take the value of the selectedOption from the SelectBox component but this current code base giving me error while I select a option from the SelectBox. The error is
Uncaught TypeError: Cannot read property 'value' of undefined
at Object.selectedOptionChangeHandler [as onChange] (PhoneNumber.jsx:22)
at Object.onChange (SelectBox.jsx:71)
at StateManager.callProp (stateManager-04f734a2.browser.esm.js:98)
at StateManager._this.onChange (stateManager-04f734a2.browser.esm.js:38)
at Select._this.onChange (Select-9fdb8cd0.browser.esm.js:1079)
at Select._this.setValue (Select-9fdb8cd0.browser.esm.js:1106)
at Select._this.selectOption (Select-9fdb8cd0.browser.esm.js:1155)
at onSelect (Select-9fdb8cd0.browser.esm.js:1732)
at HTMLUnknownElement.callCallback (react-dom.development.js:336)
at Object.invokeGuardedCallbackDev (react-dom.development.js:385)
How to get the value from onChange from the SelectBox using the current implementation?
Your SelectBox component's handleChange callback outputs the selected value versus an event, so you should consume the value.
SelectBox
handleChange = selectedOption => {
this.setState({ selectedOption });
this.props.onChange(selectedOption); // <-- sends selected value!
};
PhoneNumber
const selectedOptionChangeHandler = valueSelected => { // <-- consume valueSelected
setSelectedOption(valueSelected);
console.log({ valueSelected });
};
Related
I'm trying to validate a react-select input field with react-hook-form.(Required input) But I can't seem to do this with the code block below. Can anyone point me in the right direction?
<Controller
as={Select}
options={statusOptions}
name="status"
control={control}
className="infoItemDropdown"
rules={{ required: true }}
styles={customStyles}
/>
You can wrapper the component react-select:
import Select from 'react-select';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
const RenderSelect = (props) => {
const {
field: {
value,
onChange,
},
data,
} = props;
const [selectedOption, setSelectedOption] = useState();
const handleChange = (e) => {
onChange(e.value);
setSelectedOption(e);
};
return (
<Select
value={selectedOption}
onChange={handleChange}
options={data}
/>
);
};
RenderSelect.propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]).isRequired,
label: PropTypes.string.isRequired,
}),
).isRequired,
field: PropTypes.shape({
value: PropTypes.oneOfType([
PropTypes.string.isRequired,
PropTypes.number.isRequired,
]),
onChange: PropTypes.func.isRequired,
}),
};
RenderSelect.defaultProps = {
field: { onChange: () => {}, value: '' },
};
export default RenderSelect;
And use it in react hook form:
export default function MyForm() {
const {
handleSubmit,
formState: { isValid },
control,
} = useForm({ mode: 'onChange' });
const onSubmit = (values) =>{
console.log(values)
};
const data = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="select_something"
control={control}
rules={{ required: true }}
render={({ field }) => (<RenderSelect field={field} data={data}/>)
}
/>
<button
disabled={!isValid}
type="submit"
>
Save
</button>
</form>
);
}
This way disable the button submit if the select not has a value.
Im developing a products table with checkboxes on the side, the check box job is to send the Id of the choosen product once its clicked to it's grandfather so i can build an array of the choosen ones and send them via api.
Its built like this:
grandfather - Table page component.
father - The table itself.
child - The checkbox component.
basically I'm having trouble how to pass the information, i know i should use context api but the syntax is confusing me, here's the code:
Grandfather:
interface Istate {
Select: boolean;
SelectT: boolean;
SelectI: boolean;
Loader: boolean;
numOfProds: number;
listing: any;
checkBoxId: any;
prodName: string;
quantity: number;
price: number;
TempProdName: string;
TempQuantity: number;
TempPrice: number;
}
interface Iprops {
id: string;
name: string;
}
class Products extends Component<Iprops, Istate> {
state = {
Select: false,
SelectT: false,
SelectI: false,
Loader: false,
numOfProds: 0,
listing: Array,
checkBoxId: '',
prodName: '',
quantity: 0,
price: 0,
TempProdName: '',
TempQuantity: 0,
TempPrice: 0,
};
async componentDidMount() {
this.showLoader();
const listing = await AllProductsService.AllProducts();
this.setState({ listing });
console.log(listing);
this.setState({ numOfProds: listing.length });
this.hideLoader();
}
toggleSelect = () => {
const { Select } = this.state;
this.setState({ Select: !Select });
};
toggleSelect2 = () => {
const { SelectT } = this.state;
this.setState({ SelectT: !SelectT });
};
toggleSelect3 = () => {
const { SelectI } = this.state;
this.setState({ SelectI: !SelectI });
};
showLoader = () => {
this.setState({ Loader: true });
};
hideLoader = () => {
this.setState({ Loader: false });
};
CancelValue = () => {
const { prodName, quantity, price, TempPrice, TempProdName, TempQuantity } = this.state;
this.setState({ TempProdName: prodName });
this.setState({ TempQuantity: quantity });
this.setState({ TempPrice: price });
};
changeValue = (checkBoxId: any, value: any) => {
this.setState({
[checkBoxId]: value,
} as any);
};
render() {
const {
Select,
SelectT,
SelectI,
listing,
numOfProds,
Loader,
checkBoxId,
prodName,
quantity,
price,
TempProdName,
TempQuantity,
TempPrice,
} = this.state;
const { name, id } = this.props;
return (
<ProductTableProvider
value={{
prodName,
quantity,
price,
checkBoxId,
changeValue: this.changeValue,
}}
>
<Breadcrumb path1="/overview" one="Dashboard" path2="/dashboard/products" two="All Products" />
<InsideWrapper>
<Platform>
<h2>Products</h2>
<br />
<p>Manage your products</p>
<CardStat header="Total" numOf={numOfProds} page="Products" />
<CardStat header="Ebay" numOf={numOfProds} page="Products" />
<div className="col-sm text-center new w-100">
<div className="text-right mb-4">
<GreenButton onClick={this.toggleSelect3}>Export to Ebay</GreenButton>
<SimpleModal isOpen={SelectI} close={this.toggleSelect3} whatToDo={`Export ${name} to Ebay?`}>
<YesNoButton name={name} id={id} />
</SimpleModal>
<RegularButton onClick={this.toggleSelect}>Save</RegularButton>
<SimpleModal isOpen={Select} close={this.toggleSelect} whatToDo="Are you sure you want to save?">
<YesNoButton name={name} id={id} />
</SimpleModal>
<OrangeButton onClick={this.CancelValue}>Cancel</OrangeButton>
<DeleteButton onClick={this.toggleSelect2}>Delete</DeleteButton>
<SimpleModal isOpen={SelectT} close={this.toggleSelect2} whatToDo="Are you sure you want to delete?">
<YesNoButton name={name} id={id} />
</SimpleModal>
</div>
<Card>{Loader ? <Loading /> : <DatatablePage listing={listing} />}</Card>
</div>
</Platform>
</InsideWrapper>
</ProductTableProvider>
);
}
}
export default Products;
Father:
const DatatablePage = (props: any) => {
const { listing } = props;
const rows = Array.isArray(listing)
? listing.map((val: any) => {
return {
select: <CheckBoxComp value={false} id={val._id} />,
name: <TableText value={val.name} id={val._id} />,
barcode: val._id,
category: val.category,
image: <ImageSec src={ImageProd} />,
unitsInStock: <TableText value={val.unitsInStock} id={val._id} />,
price: <TableText value={val.price} id={val._id} />,
more: <ButtonsPopUp name={val.name} id={val._id} />,
};
})
: [];
const data = {
columns: [
{
label: '',
field: '',
sort: 'disabled',
width: 20,
},
{
label: 'Name',
field: 'name',
sort: 'asc',
width: 100,
},
{
label: 'Barcode',
field: 'barcode',
sort: 'asc',
width: 100,
},
{
label: 'Category',
field: 'category',
sort: 'asc',
width: 100,
},
{
label: 'Images',
field: 'images',
sort: 'asc',
width: 100,
},
{
label: 'Quantity',
field: 'unitsInStock',
sort: 'asc',
width: 150,
},
{
label: 'Price',
field: 'price',
sort: 'asc',
width: 150,
},
{
label: '...',
field: 'more',
sort: 'disabled',
width: 100,
},
],
rows,
};
return (
<Rules>
<MDBDataTable
entriesLabel="Show Products"
infoLabel={['Showing', 'to', 'of', 'Products']}
fixed
responsive
btn
sortable
hover
data={data}
theadColor="white"
/>
</Rules>
);
};
export default DatatablePage;
Child:
interface Istate {
checked: boolean;
products?: any[];
}
interface Iprops {
id: any;
value: any;
changeValue?: (checkBoxId: string, value: any) => void;
}
class CheckBoxComp extends Component<Iprops, Istate> {
state = {
checked: true,
products: [] as any,
};
addOne = (id: any, checked: any) => {
let { products } = this.state;
const newObj = { id, checked };
products = products.concat(newObj);
this.setState({ products }, () => {
console.log(products);
});
};
isChecked = (id: any) => {
const { checked, products } = this.state;
const { changeValue } = this.props;
console.log(id);
this.setState({
checked: !checked,
});
if (changeValue) {
changeValue(id, checked);
}
this.addOne(id, checked);
};
render() {
const { id, value } = this.props;
const { checked } = this.state;
return (
<Fragment>
<Checkbox>
<span />{' '}
<label className="checkbox-wrapper">
<span className="display" />
<Inputt type="checkbox" value={checked} onChange={this.isChecked.bind(this, id)} />
<span className="checkmark" />
</label>
</Checkbox>
</Fragment>
);
}
}
export default CheckBoxComp;
any help wrapping the code correctly using context api?
I am using redux with my react application. I am trying to get the data from my reducer but when I am trying to do this. I am getting some error.
Uncaught Error: Given action "RECEIVE_CATEGORY_NAME", reducer
"categoriesReducer" returned undefined. To ignore an action, you must
explicitly return the previous state. If you want this reducer to hold
no value, you can return null instead of undefined.
the logic written is working fine in case of influencersNameReducer but is showing an error for categoriesReducer
home_reducer.js
import { RECEIVE_INFLUENCERS_NAME, RECEIVE_CATEGORY_NAME } from './home_actions';
export const influencersNameReducer = (state = [], { type, influencers }) => {
console.log(influencers)
return type === RECEIVE_INFLUENCERS_NAME ? influencers : state
}
export const categoriesReducer = (state = [], { type, category }) => {
console.log(type, category)
return type === RECEIVE_CATEGORY_NAME ? category : state
}
home_actions.js
export const RECEIVE_INFLUENCERS_NAME = 'RECEIVE_INFLUENCERS_NAME'
export const RECEIVE_CATEGORY_NAME = 'RECEIVE_CATEGORY_NAME';
const receiveInfluencersName = influencers => ({ type: RECEIVE_INFLUENCERS_NAME, influencers })
const receiveCategoryName = categories => ({ type: RECEIVE_CATEGORY_NAME, categories })
export const fetchInfluencers = _ => dispatch => {
$.ajax({
method: 'get',
url: 'vip_api/influencers',
data: { name: _ },
success(influencers) {
dispatch(receiveInfluencersName(influencers))
},
error({ responseJSON, statusText }) {
dispatch(receiveServerErrors(responseJSON || [statusText]))
}
})
}
export const fetchCategories = _ => dispatch => {
$.ajax({
method: 'get',
url: 'vip_api/categories',
data: { name: _ },
success(categories) {
dispatch(receiveCategoryName(categories))
},
error({ responseJSON, statusText }) {
dispatch(receiveServerErrors(responseJSON || [statusText]))
}
})
}
store.js
import {influencersNameReducer, categoriesReducer} from './Vvip/Home/home_reducer';
import { composeWithDevTools } from 'redux-devtools-extension';
const reducer = combineReducers({
categoriesReducer,
influencersNameReducer,
})
const composeEnhancers = composeWithDevTools({
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
});
export default (state = {}) => (
createStore(reducer, state, composeEnhancers(applyMiddleware(errorMiddleware, timeoutMiddleware, thunk)))
)
index.js
import React, { Component } from 'react'
import Select, { components } from 'react-select'
import DateRange from '../../shared/_date_range';
import moment from 'moment';
import {ethnicities, ageRanges, isoCountries} from '../../constants';
import { connect } from 'react-redux';
import {fetchInfluencers, fetchCategories} from './home_actions';
class InfluencersForm extends Component {
constructor() {
super();
this.state = {
demography: null,
dates : {
startDate: moment(),
endDate: moment()
},
influencersName: [],
}
}
handleInfluencerName = event => {
this.props.dispatch(fetchInfluencers(event))
}
handleSelectedInfluencer = event => {
console.log(event)
this.setState({
isMenuOpenInfluencer : false
})
}
componentWillReceiveProps(newProps) {
console.log(newProps);
if (newProps.influencersNameReducer && newProps.influencersNameReducer.length) {
this.setState({
influencersName: newProps.influencersNameReducer.map((influencer, index) => {
return ({ value: influencer, label: influencer })
}),
})
}
}
handleInfluencerType = event => {
console.log(event)
}
handleInfluencerCountry = event => {
console.log(event)
}
handleInfluencerSubscribers = event => {
console.log(event)
}
handleInfluencerVideosCreated = event => {
console.log(event)
}
handleInfluencerCategory = event => {
console.log(event)
this.props.dispatch(fetchCategories(event))
}
onDemographyChange = event => {
console.log(event.currentTarget.value)
this.setState({
demography: event.currentTarget.value
})
}
handleInfluencerAge = event => {
console.log(event)
}
handleInfluencerGender = event => {
console.log(event)
}
handleInfluencerEthnicity = event => {
console.log(event)
}
updateDates = event => {
console.log(event)
this.setState({
dates: event
})
}
render() {
const influencersType = [
{ value: 'a', label: 'Type A' },
{ value: 'b', label: 'Type B' },
{ value: 'c', label: 'Type C' }
]
const influencersCategory = [
{ value: 'a', label: 'Type A' },
{ value: 'b', label: 'Type B' },
{ value: 'c', label: 'Type C' }
]
const influencersAge = ageRanges.map(age => ({ value: age, label: age }))
const influencersGender = [
{ value: 'male', label: 'Male' },
{ value: 'female', label: 'Female' }
]
const influencersKeywords = [
{ value: 'youtuber', label: 'Youtuber' },
{ value: 'vlogger', label: 'Vlogger' }
]
const influencersCountry = Object.keys(isoCountries).map(code => ({ value: code, label: isoCountries[code] }))
const DropdownIndicator = (props) => {
return components.DropdownIndicator && (
<components.DropdownIndicator {...props}>
<i className="fa fa-search" aria-hidden="true" style={{ position: 'initial', color: 'black' }}></i>
</components.DropdownIndicator>
);
};
return (
<div className='home-forms influencer-form'>
<div className='display-flex'>
<Select
options={this.state.influencersName}
onChange={this.handleSelectedInfluencer}
closeMenuOnSelect = {true}
isSearchable={true}
components={{ DropdownIndicator }}
onInputChange = {this.handleInfluencerName}
placeholder={'Start Typing Influencers Name'}
classNamePrefix="vyrill"
className="influencers influencers-icon-name" />
<Select
options={influencersType}
onChange={this.handleInfluencerType}
placeholder='Type of Influencers'
classNamePrefix="vyrill"
className="influencers influencers-icon-type" />
<Select
options={influencersCountry}
onChange={this.handleInfluencerCountry}
isSearchable={true}
components={{ DropdownIndicator }}
placeholder='Start Typing Country'
classNamePrefix="vyrill"
className="influencers influencers-icon-country" />
</div>
<div className='display-flex' style={{ marginTop: 32 }}>
<Select
options={influencersType}
onChange={this.handleInfluencerSubscribers}
placeholder='Number of Subscribers'
classNamePrefix="vyrill"
className="influencers influencers-icon-type" />
<Select
options={influencersType}
onChange={this.handleInfluencerVideosCreated}
placeholder='Number of Videos Created'
classNamePrefix="vyrill"
className="influencers influencers-icon-videos-created" />
<Select
options={influencersCategory}
onChange={this.handleInfluencerCategory}
onInputChange = {this.handleInfluencerCategory}
isSearchable={true}
components={{ DropdownIndicator }}
placeholder='Start Typing Category'
classNamePrefix="vyrill"
className="influencers influencers-icon-country influencers-icon-category" /> {/* remove influencers-icon-country later */}
</div>
<div style={{ marginTop: 50 }}>
<div className="display-flex">
<div className="icon-subscribers" style={{ marginTop: 4 }}></div>
<div style={{ fontWeight: 700, marginTop: 4 }}>Demographics</div>
<div className="radio-container">
<label>
<div style={{ fontSize: 14, marginTop: 4 }}>By influencers</div>
<input
type="radio"
name="demographics"
value="influencers"
checked={this.state.demography === 'influencers'}
onChange={this.onDemographyChange} />
<span className="custom-radio">
</span>
</label>
</div>
<div className="radio-container">
<label>
<div style={{ fontSize: 14, marginTop: 4 }}>By people in videos</div>
<input
type="radio"
name="demographics"
value="people in videos"
checked={this.state.demography === 'people in videos'}
onChange={this.onDemographyChange} />
<span className="custom-radio"></span>
</label>
</div>
</div>
</div>
<div className="display-flex" style={{ marginTop: 40 }}>
<Select
options={influencersAge}
onChange={this.handleInfluencerAge}
placeholder='Age'
classNamePrefix="vyrill"
className="influencers" />
<Select
options={influencersGender}
onChange={this.handleInfluencerGender}
placeholder='Gender'
classNamePrefix="vyrill"
className="influencers" />
<Select
options={ethnicities}
onChange={this.handleInfluencerEthnicity}
placeholder='Ethnicity'
classNamePrefix="vyrill"
className="influencers" />
</div>
<div style={{marginTop: 50}}>
<div style={{display: 'inline'}}>Contains keywords (in transcript):</div>
<span className="icon-info"></span>
<Select
options={influencersKeywords}
onChange={this.handleInfluencerName}
isSearchable={true}
classNamePrefix="vyrill"
placeholder= {" "}
className="influencers influencers-keywords"
styles = {{marginTop: 10}}/>
</div>
<div style={{marginTop: 50}} className="date-picker">
<div>Posted content time range</div>
<DateRange dates={ this.state.dates } updateDates={ this.updateDates }/>
<div className="icon-arrow-right"></div>
</div>
</div>
)
}
}
const mapStateToProps = ({ influencersNameReducer, categoriesReducer }) => ({
influencersNameReducer,
categoriesReducer
})
export default connect(mapStateToProps)(InfluencersForm)
You need to modify your reducer as:
export const influencersNameReducer = (state = [], { type, influencers }) => {
switch(type) {
case RECEIVE_INFLUENCERS_NAME:
return influencers;
default:
return state;
}
}
export const categoriesReducer = (state = [], { type, category }) => {
switch(type) {
case RECEIVE_CATEGORY_NAME:
return category;
default:
return state;
}
}
On every action the dispatcher goes to every reducer. Since in your code the influencersNameReducer reducer was not doing anything for type RECEIVE_CATEGORY_NAME thus returning undefined. So you were getting the error. Using switch case is the way to do this.
I have a container component where I have few functions, like this two for example:
newPeriodeCallback() {
const {
behandlingFormPrefix, perioder, periodeTyper, utsettelseArsaker,
} = this.props;
const {
uttakNyPeriodeFom, uttakNyPeriodeTom, uttakNyPeriodeType, uttakNyPeriodeAndel, uttakNyPeriodeArsak,
} = this.props.nyPeriode;
const getPeriodeData = (periode, periodeArray) => periodeArray
.filter(({ kode }) => kode === periode);
const periodeObjekt = getPeriodeData(uttakNyPeriodeType, periodeTyper)[0];
const arsakObjekt = getPeriodeData(uttakNyPeriodeArsak, utsettelseArsaker)[0];
const utsettelseĆ
rsak = arsakObjekt !== undefined ? {
kode: arsakObjekt.kode,
kodeverk: arsakObjekt.kodeverk,
navn: arsakObjekt.navn,
} : {};
const nyPeriode = [{
arbeidstidsprosent: uttakNyPeriodeAndel,
bekreftet: false,
fom: uttakNyPeriodeFom,
saksebehandlersBegrunnelse: null,
tom: uttakNyPeriodeTom,
utsettelseĆ
rsak,
uttakPeriodeType: {
kode: periodeObjekt.kode,
kodeverk: periodeObjekt.kodeverk,
navn: periodeObjekt.navn,
},
}];
this.props.reduxFormChange(`${behandlingFormPrefix}.UttakInfoPanel`, 'perioder', perioder.concat(nyPeriode)
.sort((a, b) => a.fom > b.fom));
this.setState({ isNyPeriodeFormOpen: !this.state.isNyPeriodeFormOpen });
}
removePeriodCallback(index) {
const { behandlingFormPrefix, perioder } = this.props;
this.props.reduxFormChange(
`${behandlingFormPrefix}.UttakInfoPanel`, 'perioder',
perioder.filter((e, i) => i === index)
.sort((a, b) => a.fom > b.fom),
);
}
I am sending these two functions as callbacks to different components:
<FieldArray
name="perioder"
component={UttakPeriode}
removePeriodCallback={this.removePeriodCallback}
inntektsmelding={inntektsmelding}
/>
<UttakNyPeriode newPeriodeCallback={this.newPeriodeCallback} />
The problem I have is when I am clicking on a button in the component where I am sending the newPeriodeCallback:
<Hovedknapp
className={styles.oppdaterMargin}
htmlType="button"
mini
onClick={newPeriodeCallback}
>
In the container component on inspecting the console I see that the removePeriodCallback is also being triggered which then removes the new period that is being added in the function newPeriodeCallback. Why is this happening, and how can I fix this?
Update
I have tried by following the suggestion in the comment, to use the arrow function in onClick, like this:
<Image
className={styles.removeIcon}
src={removePeriod}
onClick={() => removePeriodCallback(index)}
alt="Slett periode"
/>
And that has stopped the function from triggering from other places, but it is not triggering onClick either. What is wrong with this code?
This is the complete child component:
export const UttakPeriodeType = ({
bekreftet,
tilDato,
fraDato,
uttakPeriodeType,
removePeriodCallback,
index,
}) => (
<div className={classNames('periodeType', { active: !bekreftet })}>
<div className={styles.headerWrapper}>
<Element>{uttakPeriodeType.navn}</Element>
<div>
<Image src={editPeriod} alt="Rediger periode" />
<Image
className={styles.removeIcon}
src={removePeriod}
onClick={() => removePeriodCallback(index)}
alt="Slett periode"
/>
</div>
</div>
<div>
And this is the image component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import Tooltip from 'sharedComponents/Tooltip';
export class Image extends Component {
constructor() {
super();
this.state = {
isHovering: false,
};
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}
onFocus() {
this.setState({ isHovering: true });
}
onBlur() {
this.setState({ isHovering: false });
}
onKeyDown(e) {
if (e.key === 'Enter' || e.key === ' ') {
this.props.onKeyDown(e);
e.preventDefault();
}
}
render() {
const image = (<img // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
className={this.props.className}
src={this.props.src !== null ? this.props.src : this.props.imageSrcFunction(this.state.isHovering)}
alt={this.props.altCode ? this.props.intl.formatMessage({ id: this.props.altCode }) : this.props.alt}
title={this.props.titleCode ? this.props.intl.formatMessage({ id: this.props.titleCode }) : this.props.title}
tabIndex={this.props.tabIndex}
onMouseOver={this.onFocus}
onMouseOut={this.onBlur}
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
onMouseDown={this.props.onMouseDown}
/>);
if (this.props.tooltip === null) {
return image;
}
return (
<Tooltip header={this.props.tooltip.header} body={this.props.tooltip.body}>
{image}
</Tooltip>
);
}
}
Image.propTypes = {
className: PropTypes.string,
src: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape(),
]),
imageSrcFunction: PropTypes.func,
onMouseDown: PropTypes.func,
onKeyDown: PropTypes.func,
alt: PropTypes.string,
altCode: PropTypes.string,
title: PropTypes.string,
titleCode: PropTypes.string,
tabIndex: PropTypes.string,
tooltip: PropTypes.shape({
header: PropTypes.string.isRequired,
body: PropTypes.string,
}),
intl: intlShape.isRequired,
};
Image.defaultProps = {
className: '',
src: null,
imageSrcFunction: null,
onMouseDown: null,
onKeyDown: null,
tabIndex: null,
tooltip: null,
alt: null,
altCode: null,
title: null,
titleCode: null,
};
export default injectIntl(Image);
I have a react component and I want to pass down onChange as a prop to a child component, I'm using atomic design, here's my code:
HeadComponent.js
class Headcomponent extends React.Component{
constructor (props) {
super(props);
this.state = {
email: '',
password: '',
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false,
items: [],
}
}
handleUserInput = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},
() => { this.validateField(name, value) });
}
render(){
const fields= [
{
label: 'hhdghghd',
placeholder: 'fhfhhfhh 1',
ExampleMessage: this.state.formErrors.email ,
ErrorMessage: 'error message for input 1',
inputValue: this.state.email,
onChange: this.handleUserInput //this is where I'm passing my function
},
{
label: 'fffff',
placeholder: 'tttttt 2',
ExampleMessage: 'example message for second label',
ErrorMessage: 'error message for input 2',
onChange: this.handleUserInput
},
]
return (
<div>
<Form fields={fields} buttonText="Submit"/>
</div>
);
}
}
export default Headcomponent;
Form.js
const Form = props => (
<form className="Form">
{
props.fields.map((field, i) => (<LabeledInput label={field.label}
placeholder={field.placeholder}
ExampleMessage={field.ExampleMessage}
ErrorMessage={field.ErrorMessage}
onChange= {(e)=> field.onChange}
inputValue= {field.inputValue}
key={i}
passwordError={props.passwordError}
/>))
}
<Button text={props.buttonText} />
</form>
);
Form.propTypes = {
fields: PropTypes.arrayOf(PropTypes.object).isRequired,
buttonText: PropTypes.string.isRequired,
};
export default Form;
LabeledInput
const LabeledInput = props => (
<div className={`form-group `} >
<Label text={props.label} />
<Input inputValue={props.inputValue} placeholder={props.placeholder} type="text" onChange={(e)=> props.onChange} />
<ErrorMessage text={props.ErrorMessage} />
<ExampleMessage ExampleMessage={ props.ExampleMessage} />
</div>
);
LabeledInput.propTypes = {
label: PropTypes.string.isRequired,
placeholder: PropTypes.string,
onChange: PropTypes.func.isRequired,
//value: PropTypes.string.isRequired,
exampleText: PropTypes.string,
};
export default LabeledInput;
Input.js
const Input = props => (
<input type={props.type} class="form-control form-control-success is-valid" placeholder={props.placeholder} value={props.inputValue} className="form-control form-control-success"
onChange={ (e)=> props.onChange } />
);
Input.propTypes = {
inputValue: PropTypes.string,
type: PropTypes.string,
placeholder: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
export default Input;
How to pass handleUserInput from HeadComponent.js down to Input.js, so far using props I can't get it to trigger while changing text.
You forgot to actually call field.onChange method. Instead of :
onChange= {(e)=> field.onChange}
You can change it to:
onChange= {field.onChange}
I also noticed that your handleUserInput set state at :
{ [name]: e.target.value }
However, you are not setting name to any of the inputs and that's not gonna work.
Please have a look at my working sample:
const LabeledInput = props => (
<div className="form-group">
<label>{props.label}</label>
<input
className="form-control"
type="text"
placeholder={props.placeholder}
onChange={props.onChange} // That's how you have to call onChange
name={props.name} // You are not setting this prop
/>
<div>{props.ErrorMessage}</div>
<div>{props.ExampleMessage}</div>
</div>
)
const Form = ({ buttonText, fields }) => (
<form className="Form">
{fields.map((field, i) => <LabeledInput key={i} {...field} />)}
<button className="btn btn-primary">{buttonText}</button>
</form>
)
class Headcomponent extends React.Component {
constructor() {
super()
this.state = {
email: '',
password: '',
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false,
items: [],
}
this.handleUserInput = this.handleUserInput.bind(this)
}
handleUserInput(e) {
const name = e.target.name
const value = e.target.value
// Now that you have `name` and `value`, state updates are going to work
this.setState({
[name]: value
})
}
render() {
const fields = [
{
label: 'Email',
placeholder: 'email placeholder',
ExampleMessage: this.state.formErrors.email ,
ErrorMessage: 'error message for input 1',
inputValue: this.state.email,
onChange: this.handleUserInput,
name: 'email',
},
{
label: 'Password',
placeholder: 'password placeholder',
ExampleMessage: 'example message for second label',
ErrorMessage: 'error message for input 2',
onChange: this.handleUserInput,
name: 'password',
},
]
return (
<div>
<Form fields={fields} buttonText="Submit"/>
<div style={{ marginTop: 20 }}>
<div>state.email: {this.state.email}</div>
<div>state.password: {this.state.password}</div>
</div>
</div>
)
}
}
ReactDOM.render(
<Headcomponent />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div id="root"></div>