I have this message in browser:
TypeError: data.map is not a function
I am passing an array from another component here as props. What I am doing wrong?
Thank you in advance!
EDITED:
event-data.js
const months = ['January', 'February', 'March'];
const eventType = ['Party', 'Karaoke', 'Concert'];
const monthObject = [
{ id: 'sort-by-month' },
{ name: 'By month' },
{ values: months },
];
const eventObject = [
{ id: 'sort-by-category' },
{ name: 'By category' },
{ values: eventType },
];
const eventData = { monthObject, eventObject };
event-filter-bar.js
import eventData from '../../data/event-data';
class EventFilterBar extends React.Component {
render() {
return (
<FilterToolbar data={eventData} />
);
}
}
filter-toolbar.js
class FilterToolbar extends Component {
render() {
const { data } = this.props;
return (
<ButtonToolbar className="justify-content-center">
<DropdownMaker data={data} />
<DropdownWithDate />
<ResetButton />
</ButtonToolbar>
);
}
}
FilterToolbar.propTypes = {
data: PropTypes.array.isRequired,
};
dropdown-maker.js
class DropdownMaker extends Component {
render() {
const { data } = this.props;
const eventFilters = data.map((e) => (
<DropdownMenu
id={e.id}
name={e.name}
values={e.values}
key={e.id}
/>
));
return (
{ eventFilters }
);
}
}
DropdownMaker.propTypes = {
data: PropTypes.array.isRequired,
};
Check if the data is actually populated or not before map through it.
class DropdownMaker extends Component {
render() {
const { data } = this.props;
const eventFilters = (data && data.length > 0) && data.map((e) => (
<DropdownMenu
id={e.id}
name={e.name}
values={e.values}
key={e.id} //<-- don't forget to add a unique key prop while use loop
/>
));
return (
{ eventFilters }
);
}
}
DropdownMaker.propTypes = {
data: PropTypes.array.isRequired,
};
Feel free to comment if it's not working.
Related
I am using the react-select library with a list of states, and then using that to hide/show my elements
The elements are showing when I select them from the list, but then don't hide when they're removed. Here is the code:
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'ny', label: 'NY' },
{ value: 'ca', label: 'California' },
{ value: 'ak', label: 'Arkansas' }
];
export default class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
handleChange = (selectedOption) => {
if (!selectedOption) {
this.setState({})
}
else {
const result = {}
selectedOption.map((option) => {
result[option.value] = true
})
this.setState(result)
}
}
render() {
return (
<div>
<Select
isMulti
onChange={this.handleChange}
options={options}
/>
{this.state.ny && (
<div>NY State Images</div>
)}
{this.state.ca && (
<div>CA State Images</div>
)}
{this.state.ak && (
<div>AK State Images</div>
)}
</div>
)
}
}
Something is strange with that React.Component.
Try to use functional component:
export default function HomePage() {
const [state, setState] = useState({});
const handleChange = selectedOption => {
console.log("CHANGE_HAPPEN: ", selectedOption);
if (!selectedOption) {
setState({});
} else {
const result = {};
selectedOption.forEach(option => {
result[option.value] = true;
});
console.log("RESULT: ", result);
setState(prev => result);
}
};
return (
<div>
<Select isMulti onChange={handleChange} options={options} />
{state.ny && <div>NY State Images</div>}
{state.ca && <div>CA State Images</div>}
{state.ak && <div>AK State Images</div>}
</div>
);
}
class LifeCycleComps extends Component {
constructor(props) {
super(props);
this.state = {
data: 0,
names: [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}
]
};
//below is sortAlphabet function
sortAlphabet = () => {
this.setState({
names: this.state.names.sort()
});
};
//sortNames component
class SortNames extends Component {
render() {
return <span > {
this.props.names.name
} < /span>;
}
}
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<SortNames key={i} names={item} /> ))}
</ul>
Above is my code. I am not sure what is the main problem. In the above code I want to get sorted names by onClick. But I am not getting any positive results from the above snippet. Please let me know folks what I did wrong?
You can not directly use sort function in array of object. For that you need to write a sort function or write a callback function which you can modify according your need. Here is working code(https://stackblitz.com/edit/react-31un7h) :
import React, { Component } from 'react';
import { render } from 'react-dom';
const SortNames = (props) => {
return (
<span >
{props.names.name}
</span>
)
}
class LifeCycleComps extends Component {
constructor(props) {
super(props);
this.state = {
data: 0,
names: [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}
]
};
}
compare = ( a, b ) => {
if ( a.name < b.name ){
return -1;
}
if ( a.name > b.name ){
return 1;
}
return 0;
}
//below is sortAlphabet function
sortAlphabet = () => {
this.setState({
names: this.state.names.sort(this.compare)
});
};
render(){
return (
<div>
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<SortNames key={i} names={item} /> ))}
</ul>
</div>
);
}
}
//sortNames component
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div>
<LifeCycleComps/>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Here is sorted values
let arr = [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}]
function sortIt(x,y) {
if ( x.name < y.name ){
return -1;
}
if ( x.name > y.name ){
return 1;
}
return 0;
}
arr.sort(sortIt);
console.log(arr);
And here is in reactjs
sortIt(x,y) {
if ( x.name < y.name ){
return -1;
}
if ( x.name > y.name ){
return 1;
}
return 0;
}
sortAlphabet = () => {
this.state.names.sort(this.sortIt)
this.setState({
names: this.state.names.sort()
});
};
render() {
return (
<>
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<li key={i} names={item}>{item.name}</li> ))}
</ul>
</>
);
Its to just adapt the javascript code provided in the comments as below.
compare = ( a, b ) => {
if ( a.name < b.name ){
return -1;
}
if ( a.name > b.name ){
return 1;
}
return 0;
}
//below is sortAlphabet function
sortAlphabet = () => {
let objs = [...this.state.names] //create a copy here as you will not want to directly mutate the state by calling sort.
this.setState({
names: objs.sort( compare );
});
};
I've made an StackBlitz in which you could see the solution. Hope this helps.
the array is stored in local storage by clicking on the “More” button 2 news is added to DOM
How should I implement this?
const ARR= [
{
id: 1,
Name: 'Name1',
text:'lorem ipsum'
},
{
id: 2,
Name: 'Name2',
text:'lorem ipsum'
},
{
id: 3,
Name: 'Name3',
text:'lorem ipsum'
},
{
id: 4,
Name: 'Name4',
text:'lorem ipsum'
},
];
10 obj
here we save the array in localStorage
and where to execute its JSON.parse (localStorage.getItem ('news')) and I don’t understand how to implement work with local storage by click
```
localStorage.setItem('news', JSON.stringify(ARR));
class NewsOne extends PureComponent {
constructor() {
super();
this.state = {
listItem: ARR,
formAdd: false,
};
this.createItem = this.createItem.bind(this);
this.updateItem = this.updateItem.bind(this);
this.removeItem = this.removeItem.bind(this);
this.addForm = this.addForm.bind(this);
}
updateItem(item) {
const { listItem } = this.state;
this.setState({
listItem: listItem.map(elem => (
elem.id === item.id ? item : elem
))
});
}
removeItem(itemId) {
const { listItem } = this.state;
this.setState({
listItem: listItem.filter(item => item.id !== itemId)
});
}
createItem(item) {
const { listItem } = this.state;
this.setState({
listItem: [item, ...listItem],
});
}
addForm() {
const { formAdd } = this.state;
this.setState({
formAdd: !formAdd,
})
}
render() {
const { listItem, formAdd } = this.state;
return(
<>
<div className="box">
<Title />
<List
data={listItem}
removeFromProps={this.removeItem}
updateFromProps={this.updateItem}
/>
</div>
<button className ="addnews" onClick = {this.addForm}>
Add
</button>
</>
);
}
}
A class that defaultly displays 2 elements on page.
I tried here to interact with the array from localStorage, but it fails
class List extends PureComponent {
constructor() {
super();
this.state = {
count: 2,
}
this.addObj = this.addObj.bind(this);
}
addObj() {
const { count } = this.state;
this.setState({
count: count + 2,
});
}
render() {
const { count } = this.state;
const { data } = this.props;
return(
<>
<ul>
{
data.slice(0, count).map(item => (
<>
<Item
key={item.id}
item={item}
/>
</>
))
}
</ul>
<button className ="addnews" onClick = {this.addObj}>
More
</button>
</>
);
}
}
Expected effect:
click button -> call function save -> pass object p to function update
update second object{a: 'purple', desc: 'grt', date: '12 -10-2019 '} in colors array, which is in theproducts array
Before update: {a: 'purple', desc: 'grt', date: '12 -10-2019 '}
After update: {a: 'violet', desc: 'gt', date: '12 -12-1980 '}
Error in console.log:
Uncaught TypeError: this.props.product.colors.map is not a function
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}]
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a: 'white', {a:'black', desc: 'grtrt', date: '12-12-2119'}, }, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0 //It is first object in products array
index: 1 //It is second object in colors array
}
}
update = (item) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: item}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
Items
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
</div>
);
}
}
Item
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
You need to pass the index of the colors item and then update it accordingly
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
and then in the topmost parent
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorsIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
Working demo
const { Component } = React;
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}],
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a:'black', desc: 'grtrt', date: '12-12-2119'}, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0,
index: 1
}
}
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
}
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
);
}
}
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<pre>{JSON.stringify(this.props.time)}</pre>
<button onClick={this.save}>Save</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />
I have created two separate components and a parent component. I am trying to see how I can connect them so that I can have the dropdown for the children vanish when their checkbox is unchecked. I think I may have created this so the 2 components can't communicate, but I wanted to see if there was a way to get them to. Been trying different ways, but cannot seem to figure it out.
This is the parent component. It builds sections from some data and renders a checkbox treeview with the first (parent) checkbox having a dropdown. When the third option is selected in this dropdown, it renders in a dropdown for each child checkbox. I am trying to see if I can have the child dropdowns vanish when the checkbox is unchecked, but I can't seem to get the 2 components to communicate.
export default class CheckboxGroup extends PureComponent {
static propTypes = {
data: PropTypes.any.isRequired,
onChange: PropTypes.func.isRequired,
counter: PropTypes.number,
};
mapParents = (counter, child) => (
<li key={child.get('name')} className='field'>
<SegmentHeader style={segmentStyle} title={child.get('label')} icon={child.get('icon')}>
<div className='fields' style={zeroMargin}>
<div className='four wide field'>
<TreeCheckbox
label={`Grant ${child.get('label')} Permissions`}
counter={counter}
onChange={this.props.onChange}
/>
{child.get('items') && this.buildTree(child.get('items'), counter + child.get('name'))}
</div>
<div className='twelve wide field'>
<GrantDropdown label={child.get('label')} childItems={child.get('items')}/>
</div>
</div>
</SegmentHeader>
</li>
)
mapDataArr = (counter) => (child) => (
(counter === 0 || counter === 1000) ?
this.mapParents(counter, child)
:
<li key={child.get('name')}>
<TreeCheckbox label={child.get('label')} onChange={this.props.onChange}/>
{child.get('items') && this.buildTree(child.get('items'), counter + child.get('name'))}
</li>
)
buildTree = (dataArr, counter) => (
<ul key={counter} style={listStyle}>
{dataArr.map(this.mapDataArr(counter))}
</ul>
)
render() {
return (
<div className='tree-view'>
{this.buildTree(this.props.data, this.props.counter)}
</div>
);
}
}
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
const pointer = { cursor: 'pointer' };
class TreeCheckbox extends PureComponent {
static propTypes = {
onChange: PropTypes.func,
label: PropTypes.string,
currentPerson: PropTypes.any,
};
componentDidMount() {
if (this.props.currentPerson.get('permissions').includes(this.props.label)) {
this.checkInput.checked = true;
this.changeInput(this.checkInput);
}
}
getLiParents = (el, parentSelector) => {
if (!parentSelector) parentSelector = document; // eslint-disable-line
const parents = [];
let parent = el.parentNode;
let o;
while (parent !== parentSelector) {
o = parent;
if (parent.tagName === 'LI') parents.push(o);
parent = o.parentNode;
}
return parents;
}
traverseDOMUpwards = (startingEl, steps) => {
let elem = startingEl;
for (let i = 0; i < steps; i++) {
elem = elem.parentNode;
}
return elem;
}
markIt = (nodeElem, checkIt, indeter) => {
const node = nodeElem;
const up = this.traverseDOMUpwards(node, 1);
node.checked = checkIt;
node.indeterminate = indeter;
this.props.onChange(up.children[1].innerText, checkIt);
}
changeInput = (event) => {
const e = event === this.checkInput ? event : event.target;
const selector = 'input[type="checkbox"]';
const querySelector = (el) => el.querySelectorAll(selector);
const container = this.traverseDOMUpwards(e, 2);
const markAllChildren = querySelector(container.parentNode);
const checked = e.tagName === 'LABEL' ? !markAllChildren[0].checked : e.checked;
const siblingsCheck = (element) => {
let onesNotRight = false;
const sibling = [].slice.call(element.parentNode.children);
sibling.filter(child => child !== element).forEach(elem => {
if (querySelector(elem)[0].checked !== querySelector(element)[0].checked) {
onesNotRight = true;
}
});
return !onesNotRight;
};
const checkRelatives = (ele) => {
let el = ele;
if (el.tagName === 'DIV') el = el.parentNode;
if (el.tagName !== 'LI') return;
const parentContainer = this.traverseDOMUpwards(el, 2);
if (siblingsCheck(el) && checked) {
this.markIt(querySelector(parentContainer)[0], true, false);
checkRelatives(parentContainer);
} else if (siblingsCheck(el) && !checked) {
const parent = this.traverseDOMUpwards(el, 2);
const indeter = parent.querySelectorAll(`${selector}:checked`).length > 0;
this.markIt(querySelector(parent)[0], false, indeter);
checkRelatives(parent);
} else {
for (const child of this.getLiParents(el)) {
this.markIt(querySelector(child)[0], false, true);
}
}
};
for (const children of markAllChildren) {
this.markIt(children, checked, false);
}
checkRelatives(container);
};
getRef = (input) => { this.checkInput = input; }
render() {
const { label } = this.props;
return (
<div className='permission-item'>
<div className='ui checkbox'>
<input type='checkbox' onChange={this.changeInput} ref={this.getRef}/>
<label onClick={this.changeInput} style={pointer}>
{label}
</label>
</div>
</div>
);
}
}
const mapStatetoProps = (state) => ({
currentPerson: state.get('currentPerson'),
});
export default connect(mapStatetoProps)(TreeCheckbox);
class GrantDropdown extends AbstractSettingsComponent {
static propTypes = {
label: PropTypes.string,
currentPerson: PropTypes.any,
counter: PropTypes.number,
permissionOptions: PropTypes.any,
};
state = {
items: new List(),
}
componentDidMount() {
if (this.props.childItems) {
this.getAllChildLabels(this.props.childItems);
}
}
getAllChildLabels = (childItems) => {
let list = new List();
for (const item of childItems) {
list = list.push(item.get('label'));
if (item.get('items')) {
for (const childItem of item.get('items')) {
list = list.push(childItem.get('label'));
}
}
}
this.setState({ items: list });
}
handlePermissionChange = (label) => (e, { value }) => {
this.updatePerson(['locationsPermissionsMap', label], value);
}
mapItems = (val, i) => { // eslint-disable-line
const locationVal = this.props.currentPerson.getIn(['locationsPermissionsMap', val]);
return (
<div className={locationVal === 2 ? 'two fields' : 'field'} style={zeroMarginBottom} key={i}>
<OptionSelector
options={this.firstThreePermissionOpt()}
defaultValue={locationVal || 0}
onChange={this.handlePermissionChange(val)}
/>
{locationVal === 2 &&
<div className='field' style={zeroMarginBottom}>
<LocationMultiSelect name={val} {...this.props}/>
</div>
}
</div>
);
}
render() {
const { label, currentPerson } = this.props;
if (!currentPerson.get('permissions').includes(label)) {
return null;
}
const locationLabel = currentPerson.getIn(['locationsPermissionsMap', label]);
return (
<div className={ locationLabel === 2 ? 'two fields' : 'field'} style={zeroMarginBottom}>
<div className='field'>
<OptionSelector
options={this.getPermissionOptions()}
defaultValue={currentPerson.getIn(['locationsPermissionsMap', label]) || 0}
onChange={this.handlePermissionChange(label)}
/>
{locationLabel === 3 && this.state.items.map(this.mapItems)}
</div>
{locationLabel === 2 &&
<div className='field'>
<LocationMultiSelect name={label} {...this.props}/>
</div>
}
</div>
);
}
}
const mapStatetoProps = (state) => ({
currentPerson: state.get('currentPerson'),
locations: state.get('locations'),
});
export default connect(mapStatetoProps)(GrantDropdown);
What you can do is set couple of props to send to child component to re-render them.
Example
export default class CheckBoxComponent extends React.Component {
changeInput() {
this.props.onCheckedChanged();
}
render() {
return(
<div className='permission-item'>
<div className='ui checkbox'>
<input type='checkbox' onChange={this.changeInput} ref={this.getRef}/>
<label onClick={this.changeInput.bind(this)} style={pointer}>
{label}
</label>
</div>
</div>
)
}
}
export default class DropDownComponent extends React.Component {
renderSelect() {
// here render your select and options
}
render() {
return(
<div>
{this.props.checkboxChecked === false ? this.renderSelect : null}
</div>
)
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxChecked: false
};
}
onCheckedChanged() {
this.setState({ checkboxChecked: !this.state.checkboxChecked });
}
render() {
return(
<div>
<CheckBoxComponent onCheckedChanged={this.onCheckedChanged.bind(this)} />
<DropDownComponent checkboxChecked={this.state.checkboxChecked} />
</div>
)
}
}
You can set the <input/> name attribute into a corresponding property in your state so the handler can get the name of the list / input via the event parameter and set the state respectively.
Then you can conditionally render the Dropdown according to the state.
Here is a small example of such behavior:
const lists = [
[
{ value: "0", text: "im 0" },
{ value: "1", text: "im 1" },
{ value: "2", text: "im 2" }
],
[
{ value: "a", text: "im a" },
{ value: "b", text: "im b" },
{ value: "c", text: "im c" }
]
];
const DropDown = ({ options }) => {
return (
<select>
{options.map(opt => <option value={opt.value}>{opt.text}</option>)}
</select>
);
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showList0: false,
showList1: true
};
this.toggleCheck = this.toggleCheck.bind(this);
}
toggleCheck(e) {
const listName = e.target.name;
this.setState({ [listName]: !this.state[listName] });
}
render() {
return (
<div>
{lists.map((o, i) => {
const listName = `showList${i}`;
const shouldShow = this.state[listName];
return (
<div>
<input
type="checkbox"
name={listName}
checked={shouldShow}
onChange={this.toggleCheck}
/>
{shouldShow && <DropDown options={o} />}
</div>
);
})}
</div>
);
}
}
ReactDOM.render(<App />, 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>
<div id="root"></div>