I need to render plenty of inputs for every name of the array, but the problem is creating dynamically useState const and onChange handler for every rendered input.
I try to create handleChange key for every item in an array and pass it to input onChange but got the "String Instead Fuction" error. How to resolve the problem in another way and also avoid code duplication?
export const myArr = [
{ name: "Crist", data: "crist", color: "lightblue", handleChange: "cristHandleChange"},
{ name: "Ruby", data: "ruby", color: "lightpink", handleChange: "rubyHandleChange"},
{ name: "Diamond", data: "diamond", color: "white", handleChange: "diamondHandleChange"},
];
export const myComponent = () => {
const [cristQuantity, setCristQuantity] = useState(0);
const [rubyQuantity, setRubyQuantity] = useState(0);
const [diamondQuantity, setDiamondQuantity] = useState(0);
function cristHandleChange(event) {
setCristQuantity(event.target.value);
}
function rubyHandleChange(event) {
setRubyQuantity(event.target.value);
}
function diamondHandleChange(event) {
setDiamondQuantity(event.target.value);
}
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={item.handleChange} //???
/>
</div>
))
: null}
</>
);
};
You should create one handler for all inputs, and save values in a object with a key as item.data. Such way: {crist: 1, ruby: 3, diamond: 5}
export const myArr = [
{
name: "Crist",
data: "crist",
color: "lightblue"
},
{
name: "Ruby",
data: "ruby",
color: "lightpink"
},
{
name: "Diamond",
data: "diamond",
color: "white"
}
];
export function MyComponent() {
// Lazy initial state
// https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [quantities, setQuantities] = useState(() =>
myArr.reduce((initialQuantities, item) => {
initialQuantities[item.data] = 0;
return initialQuantities;
}, {})
);
// common handler for every onChange with computed property name
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names
const handleChange = (event) => {
setQuantities((prevQuantities) => ({
...prevQuantities,
[event.target.name]: event.target.value
}));
};
return (
<>
{Array.isArray(myArr)
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={handleChange}
value={quantities[item.data] || ""}
/>
</div>
))
: null}
</>
);
}
I haven't actually tested this but this is something I would do which will get you on the way.
export const myArr = [
{
name: `Crist`,
data: `crist`,
color: `lightblue`,
handleChange: `cristHandleChange`,
},
{
name: `Ruby`,
data: `ruby`,
color: `lightpink`,
handleChange: `rubyHandleChange`,
},
{
name: `Diamond`,
data: `diamond`,
color: `white`,
handleChange: `diamondHandleChange`,
},
]
export const myComponent = () => {
const [quantities, setQuantities] = useState({
crist: 0,
ruby: 0,
diamond: 0,
})
const onChangeHandler = ({ name, value }) => {
setQuantities((prevState) => ({ ...prevState, [name]: value }))
}
return (
<>
{myArr.length > 0
? myArr.map(({ data, name, color }, index) => {
// if you need to do an update to push more items to the object for any dynamic reason
if (!quantities.name)
setQuantities((prevState) => ({ ...prevState, [name]: 0 }))
return (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={data}>{name}</label>
<input
type="number"
name={name}
value={quantities.name}
style={{ background: color }}
onChange={(e) =>
onChangeHandler({ name, value: e.currentTarget.value })
}
/>
</div>
)
})
: null}
</>
)
}
You're passing a string (not a function) to onChange which is causing that problem.
To fix it, you can wrap all functions into another object
const onChangeFunctions = {
cristHandleChange: (event) => {
setCristQuantity(event.target.value);
},
rubyHandleChange: (event) => {
setRubyQuantity(event.target.value);
},
diamondHandleChange: (event) => {
setDiamondQuantity(event.target.value);
},
};
and call onChangeFunctions[item.handleChange] like below
export const myArr = [
{
name: "Crist",
data: "crist",
color: "lightblue",
handleChange: "cristHandleChange",
},
{
name: "Ruby",
data: "ruby",
color: "lightpink",
handleChange: "rubyHandleChange",
},
{
name: "Diamond",
data: "diamond",
color: "white",
handleChange: "diamondHandleChange",
},
];
export const myComponent = () => {
const [cristQuantity, setCristQuantity] = useState(0);
const [rubyQuantity, setRubyQuantity] = useState(0);
const [diamondQuantity, setDiamondQuantity] = useState(0);
const onChangeFunctions = {
cristHandleChange: (event) => {
setCristQuantity(event.target.value);
},
rubyHandleChange: (event) => {
setRubyQuantity(event.target.value);
},
diamondHandleChange: (event) => {
setDiamondQuantity(event.target.value);
},
};
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={onChangeFunctions[item.handleChange]}
/>
</div>
))
: null}
</>
);
};
Let me start by saying that you might want to use a library like React Hook Form for this, although, if this is the only form and you don't need all the fancy features (or additional bundle size), you can do it like this as well.
The first step is to store your form data in an Object. After this change, you can use a single useState({}) to store and read your form data and drastically simplifies your handlers.
For example:
export const myArr = [
{ name: "Crist", data: "crist", color: "lightblue"},
{ name: "Ruby", data: "ruby", color: "lightpink"},
{ name: "Diamond", data: "diamond", color: "white"},
];
// generate Object with data and initialValue or empty string
// e.g. `{ 'crist': '', 'ruby': '', 'diamond': '' }`
const getInitialFormValues = (arr) => Object.fromEntries(
arr.map(({ data, initialValue }) => [data, initialValue || ''])
);
export const MyComponent = () => {
const [formValues, setFormValues] = useState(getInitialFormValues(myArr));
function handleChange(event) {
// update the changed form value
setFormValues(current => ({
...current, // other form values
[event.target.name]: event.target.value // updated value
}));
}
return (
<>
{myArr
? myArr.map((item, index) => (
<div className="main-inputs__wrapper" key={`item${index}`}>
<label htmlFor={item.data}>{item.name}</label>
<input
type="number"
name={item.data}
style={{ background: item.color }}
onChange={handleChange}
value={formValues[item.data]}
/>
</div>
))
: null}
</>
);
};
Related
I am trying to access the students variable by putting its value in a state then accessing that state in my handler function but it always returns an empty array.
const students = useSelector((state) => state.students);
useEffect(() => {
setdata(students);
console.log(data);
}, [])
const handleScanWebCam = (result, error) => {
if (result) {
console.log(data)
const list = data.filter(function (student) {
return student.id === result.text;
})
setScanResultWebCam({
id: list.id,
grade: list.grade,
name: list.name,
section: list.section
});
}
}
this is my full code
function QrScanner() {
const dispatch = useDispatch(getStudents())
useEffect(() => {
dispatch(getStudents());
}, [dispatch])
const students = useSelector((state) => state.students);
const [scanResultWebCam, setScanResultWebCam ] = useState({
id: '',
grade: '',
name: '',
section:''
});
const [openVerifier, setOpenVerifier] = useState(false);
const [data, setdata] = useState([]);
useEffect(() => {
setdata(students);
console.log(data);
}, [])
const handleScanWebCam = (result, error) => {
if (result) {
const list = data.filter(function (student) {
return student.id === result.text;
})
setScanResultWebCam({
id: list.id,
grade: list.grade,
name: list.name,
section: list.section
});
setOpenVerifier(true);
}
}
return (
<>
<NavBar />
<Container
sx={{
display: 'flex',
marginTop: '4rem',
flexWrap: 'wrap',
gap: '12px',
width: '90%'
}}
>
<Box
sx={{
width: '50%',
border: 'solid',
display: 'flex',
flex: '1'
}}
>
<QrReader
scanDelay={500}
containerStyle={{ width: '25rem', margin: 'auto'}}
onResult={handleScanWebCam}
// onError={handleErrorWebcam}
/>
</Box>
<PopupVerifier
details={scanResultWebCam}
verifier={openVerifier}
handleClose={() => handleClose()}
/>
</Container>
</>
)
}
If you need to cache a "duplicate" of the selected students state then I'd recommend caching students in a React ref that can have its current value read at any time during the component lifecycle, especially in stale closures.
Example:
function QrScanner() {
const dispatch = useDispatch(getStudents());
useEffect(() => {
dispatch(getStudents());
}, [dispatch]);
const students = useSelector((state) => state.students);
const studentsRef = React.useRef(students);
useEffect(() => {
studentsRef.current = students;
}, [students]);
...
const handleScanWebCam = (result, error) => {
if (result) {
const list = studentsRef.current.find(
(student) => student.id === result.text
);
if (list) {
setScanResultWebCam(list);
}
setOpenVerifier(true);
}
};
return (
...
);
}
You have using from react-redux. so you dont need to set student state.
and dispatch is used for change in store not getting data from it.
if you want to get an item from array use find instead of filter :
const list = students.find(student=>student.id===result.text);
i edit your code to :
function QrScanner() {
const students = useSelector((state) => state.students);
const [scanResultWebCam, setScanResultWebCam ] = useState({
id: '',
grade: '',
name: '',
section:''
});
const [openVerifier, setOpenVerifier] = useState(false);
const handleScanWebCam = (result, error) => {
if(result) {
const list = students.find(student=> student.id === result.text);
setScanResultWebCam({id: list.id, grade: list.grade, name: list.name, section: list.section});
setOpenVerifier(true);
}
}
return(
...
)
}
Feeding the react-select component the selected state to the value prop, makes it unable to select anything at all? I'm trying to feed the value-prop with the selected state so that i can reset react-select via setState. What am i doing wrong here?
Select Component with isMulti option:
import SelectForm from "react-select";
const Select = (props) => {
const options = props.options.map((option) => ({
value: option,
label: option.toLowerCase(),
}));
const costumStyles = {
container: (baseStyles) => ({ ...baseStyles, width: "100%" }),
control: (baseStyles) => ({
...baseStyles,
backgroundColor: "transparent",
borderStyle: "none",
borderRadius: "15px",
marginTop: "3px",
}),
singleValue: (baseStyles) => ({ ...baseStyles, color: "white" }),
};
const handleChange = (event) => {
props.onChangeOption(event);
};
return (
<div>
{props.isMulti ? (
<SelectForm
options={options}
onChange={handleChange}
styles={costumStyles}
placeholder={props.placeholder}
value={props.value}
isMulti
/>
) : (
<SelectForm
options={options}
onChange={handleChange}
styles={costumStyles}
placeholder={props.placeholder}
value={props.value}
/>
)}
</div>
);
};
export default Select;
The parent component which uses the Select component:
import React, { useState } from "react";
import Select from "../../input/Select";
const CreateJobPage3 = (props) => {
const [department, setDepartment] = useState();
const [subDepartment, setSubDepartment] = useState([]);
const departments = [jobDepartment1, jobDepartment2, jobDepartment3];
const subDepartments = [jobSubDepartment1, jobSubDepartment2, jobSubDepartment3];
const departmentHandler = (props) => {
setDepartment(props.value);
setSubDepartment([]);
};
const subDepartmentHandler = (props) => {
setSubDepartment(props.map((item) => item.value));
};
return (
<>
<Select
placeholder="Choose Department"
options={departments}
onChangeOption={departmentHandler}
value={department || ""}
/>
<Select
placeholder="Choose Sub-Departments"
options={subDepartments}
onChangeOption={subDepartmentHandler}
isMulti={true}
value={subDepartment || ""}
/>
</>
);
};
export default CreateJobPage3;
According to the docs of react-select they're expecting complete option to be passed in value array.
Say for example, we have options like
const options=[
{ value: 'blue', label: 'Blue', color: '#0052CC', isDisabled: true },
{ value: 'red', label: 'Red', color: '#0052CC', isDisabled: true }
];
then we have to pass value as,
value={[{ value: 'blue', label: 'Blue', color: '#0052CC', isDisabled: true }]}
Refer this example: https://codesandbox.io/s/17g0yy?module=/example.tsx
Try this,
const departmentHandler = (props) => {
setDepartment(props);
setSubDepartment([]);
};
const subDepartmentHandler = (props) => {
setSubDepartment(props);
};
I have a simple problem here which I can't figure out. I wanted to hide menus depending on the condition.
For example if status contains at least one "Unlinked". "All unlinked images" menu should appear. I did used .some and I wonder why it doesn't return a boolean.
Codesandbox is here Click here
const showDeleteAllInvalidButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Invalid");
};
const showDeleteAllUnlinkedButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Unlinked");
};
The methods do return a boolean. But in the menus array you are assigning a function reference not the result -
show: showDeleteAllInvalidButton // function reference
show is now assigned a reference to the function showDeleteAllInvalidButton not the result of productImages?.some. You need to invoke the functions when assigning -
show: showDeleteAllInvalidButton() // result of productImages?.some
In your menus object you have a key that contains a function, so if you want this function to filter out your elements you need to execute the show method in side the filter method.
import React, { useState } from "react";
import Button from "#mui/material/Button";
import MenuItem from "#mui/material/MenuItem";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
import CustomMenu from "../../Menu";
const products = [
{
productName: "Apple",
productImages: [
{
status: "Unlinked"
}
]
},
{
productName: "Banana",
productImages: [
{
status: "Unlinked"
}
]
},
{
productName: "Mango",
productImages: [
{
status: "Unlinked"
},
{
status: "Unlinked"
}
]
}
];
const HeaderButtons = () => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const showDeleteAllInvalidButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Invalid");
};
const showDeleteAllUnlinkedButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Unlinked");
};
const menus = [
{
id: 1,
name: "Invalid images",
action: () => {
handleClose();
},
show: showDeleteAllInvalidButton
},
{
id: 2,
name: "Unlinked images",
action: () => {
handleClose();
},
show: showDeleteAllUnlinkedButton
},
{
id: 3,
name: "All images",
action: () => {
handleClose();
},
show: () => true // not that I changed it to a function for consistency, but you can check for type in the filter method instead of running afunction
}
];
return (
<div>
<Button
color="error"
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
variant="outlined"
onClick={handleClick}
endIcon={<KeyboardArrowDownIcon />}
>
Options
</Button>
<CustomMenu anchorEl={anchorEl} open={open} onClose={handleClose}>
{menus
.filter((e) => e.show()) // here is your mistake
.map(
({
id = "",
action = () => {},
icon = null,
name = "",
divider = null
}) => (
<>
<MenuItem key={id} onClick={action} disableRipple>
{icon}
{name}
</MenuItem>
{divider}
</>
)
)}
</CustomMenu>
</div>
);
};
export default HeaderButtons;
In your code, it will always render because your filter functions are evaluating as truth.
I have an array set in state like:
const Theme = {
name: "theme",
roots: {
theme: Theme,
},
state: {
theme: {
quiz: {
quizGender: null,
quizSleepComfort: {
justMe: {
soft: null,
medium: null,
firm: null,
},
partner: {
soft: null,
medium: null,
firm: null,
}
},
},
},
},
actions: {
// ...
},
};
I then have a component that has checkboxes, one for soft, medium, and firm. The code for the component is:
const Question = ({ state }) => {
const [checkedItems, setCheckedItems] = useState([]);
const checkboxes = [
{
label: "Firm",
value: "firm",
},
{
label: "Medium",
value: "medium",
},
{
label: "Soft",
value: "soft",
},
];
state.theme.quiz.quizSleepComfort.justMe = checkedItems;
return (
<QuestionCommonContainer>
{checkboxes.map((item, id) => (
<QuizCheckbox
label={item.label}
name={item.label}
value={item.value}
selected={checkedItems[item.value] === true}
onChange={(e) => {
setCheckedItems({
...checkedItems,
[e.target.value]: e.target.checked,
});
}}
/>
))}
</QuestionCommonContainer>
);
};
export default connect(Question);
This specific component is just interacting with state.theme.quiz.quizSleepComfort.justMe object, not the partner object.
As of right now when a checkbox is selected, let's say the checkbox for "firm" is checked, the state gets updated to what looks like this:
...
quizSleepComfort: {
justMe: {
firm: true,
},
partner: {
soft: null,
medium: null,
firm: null,
}
},
...
I am trying to figure out how I would be able to alter this components code so that instead of setting the justMe object to include only the items that are checked (in this case "firm"), it should keep the other items as well ("soft", "medium") as null.
Please let me know if there is more info i should provide.
Okay. So the following is bad practice
state.theme.quiz.quizSleepComfort.justMe = checkedItems;
You should pass a function to the Question component, something like onChange.
The onChange function should update the state in your parent component. Use the spread operator ... to get a copy of the old object. for example
const onChange = (newState) =>
setState((oldState) => ({
...oldState,
justMe: { ...oldState.justMe, ...newState },
}));
the resulting object will contain all the properties of the original state but will overwrite any property set on newState in justMe. If the property that you want to update is more nested, just repeat the steps of spreading.
--- UPDATE ---
I have added an example that I think is close to what you are trying to achieve.
const Parent = () => {
const [state, setState] = useState(initialState);
const onChange = useCallback(
(newState) =>
setState((oldState) => ({
...oldState,
theme: {
...oldState.theme,
quiz: {
...oldState.theme.quiz,
quizSleepComfort: {
...oldState.theme.quizSleepComfort,
justMe: {
...oldState.theme.quizSleepComfort.justMe,
...newState,
},,
},
},
},
})),
[],
);
return <Question onChange={onChange} />;
};
const checkboxes = [
{
label: 'Firm',
value: 'firm',
},
{
label: 'Medium',
value: 'medium',
},
{
label: 'Soft',
value: 'soft',
},
];
const Question = ({ onChange }) => {
const [checkedItems, setCheckedItems] = useState([]);
useEffect(() => {
onChange(checkedItems);
}, [checkedItems, onChange]);
return (
<QuestionCommonContainer>
{checkboxes.map((item, id) => (
<QuizCheckbox
label={item.label}
name={item.label}
value={item.value}
selected={checkedItems[item.value] === true}
onChange={(e) => {
setCheckedItems((oldCheckedItems) => ({
...oldCheckedItems,
[e.target.value]: e.target.checked,
}));
}}
/>
))}
</QuestionCommonContainer>
);
};
export default connect(Question);
As you are having a really nested object to update, it might be worth taking a look at Object.assign
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.