React doesn't see mapped props - javascript

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

Hide/show elements with react-select - elements not hiding

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>
);
}

How to sort Array Data in ReactJs

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.

Add items to page with localstorage

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>
</>
);
}
}

Updating nested objects in nested arrays in React

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" />

reactjs components communicating

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>

Categories

Resources