I am trying to display a toggle checkbox for every JSON value I have. This is what the json object looks like for element
{
"sourceIP": {
"Primary": ["237.100.100.3", "238.0.4.8"],
"Secondary": ["237.0.1.178", "237.1.1.91"]
},
"multicastIP": {
"Primary": ["10.208.153.129", "238.0.4.8"],
"Secondary": ["10.208.133.58", "238.0.4.8"]
}
}
So I would like to iterate through element instead of hardcoding it like this:
const CustomToggle = ({ element}) => (
<List divided verticalAlign='middle'>
<Segment textAlign='left' style={{paddingLeft: '7.5em'}}>
<Checkbox
toggle
label={(JSON.stringify(element.sourceIP.Primary[0]))}
value={(JSON.stringify(element.sourceIP.Primary[0]))}
/>
<Checkbox
toggle
label={(JSON.stringify(element.sourceIP.Primary[1]))}
value={(JSON.stringify(element.sourceIP.Primary[1]))}
/>
<Checkbox
toggle
label={(JSON.stringify(element.sourceIP.Secondary[1]))}
value={(JSON.stringify(element.sourceIP.Secondary[1]))}
/>
</Segment>
</List>
);
I have been trying to forEach and .map but none of this seems to work. I don't want to have to hardcode every json value in element. How can I just iterate through my element JSON object?
Object.keys(element).reduce((a,c) => a.concat(...Object.values(element[c])),[])
this transformed data gives array of ip adtresses. So, now you can iterate it
const CustomToggle = ({ element}) => {
const elements = Object.keys(element).reduce((a,c) => a.concat(...Object.values(element[c])),[]);
return (
<List divided verticalAlign='middle'>
<Segment textAlign='left' style={{paddingLeft: '7.5em'}}>
{elements.map(ip => <Checkbox toggle label={ip} value={ip} />}
</Segment>
</List>
)
}
Here try this, let me know if it helps :)
const CustomToggle = ({ element }) => (
<List divided verticalAlign="middle">
<Segment textAlign="left" style={{ paddingLeft: "7.5em" }}>
{Object.keys(element).map(key => {
return [...element[key].Primary, ...element[key].Secondary].map(x => {
<Checkbox toggle label={x} value={x} />;
});
})}
</Segment>
</List>
);
You can for example do something like the snipped below:
const List = ({ divided, rest }) => <div {...rest} />;
const Checkbox = ({ toggle, ...props }) => (
<div>
<input type="checkbox" {...props} />
<label>{props.label}</label>
</div>
);
const Segment = (props) => <div {...props} />;
const CustomToggle = ({ element }) => (
<div>
{element.map((ip) => (
<Checkbox toggle label={ip} value={ip} />
))}
</div>
);
function App() {
let result = `{
"sourceIP": {
"Primary": ["237.100.100.3", "238.0.4.8"],
"Secondary": ["237.0.1.178", "237.1.1.91"]
},
"multicastIP": {
"Primary": ["10.208.153.129", "238.0.4.8"],
"Secondary": ["10.208.133.58", "238.0.4.8"]
}
}`;
let data = JSON.parse(result);
let ips = [];
Object.keys(data).map((source) => {
Object.keys(data[source]).map((type) => {
ips.push(...data[source][type]);
});
});
return <CustomToggle element={ips} />;
}
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
So, you'll parse the data, then iterate over it to get the IPs and then pass those as a prop to the CustomToggle. The code above is just an example to get you going as I don't know what your List, Checkbox and Segment components look like.
The reasoning is that CustomToggle should not know or care what the data should look like, it just knows how to render a custom toggle based on some string that it receives, in this case the ip. You should however make sure that each Checkbox has a unique key.
Related
I'm working on a project using react, next.js, antd.
However, the following error occurred:
Warning: Each child in a list should have a unique "key" prop.
We know that the following error occurs because the list's child element does not have a unique key.
So, I tried to fix the code where the error occurred by using the following methods, but it was not resolved.
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};
------------------------------
try 0.
<List.Item>
<BoardCard post={item} key={item.id} />
</List.Item>
try 1.
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
try 2.
renderItem={(item, i) => (
<List.Item key={i}>
<BoardCard post={item} />
</List.Item>
try 3.
<List.Item>
<div key={item.id}>
<BoardCard post={item} />
</div>
</List.Item>
try 4.
<div key={item.id}>
<List.Item>
<BoardCard post={item} />
</List.Item>
</div>
So I start to doubt whether the above problem is a problem with the component structure.
The component where the problem occurred consists of the following three components.
// pages/profile
import React from 'react';
import NicknameEditForm from './NicknameEditForm';
import MyScrap from './MyScrap';
import MyBoard from './MyBoard';
const MyInfo = () => {
return (
<section>
<NicknameEditForm /> // A component that changes a member's nickname
<MyScrap /> // Gets the post that has been liked from the POST table.
<MyBoard /> // I get the post I wrote from the POST table.
</section>
)
};
export default MyInfo;
// MyScrap.js
const { likedPosts } = useSelector((state) => state.post);
// MyBoard.js
const { boardPosts } = useSelector((state) => state.post);
Also, MyScrap and MyBoard components receive the following data from the backend and use it.
// likedPosts used in MyScrap component
likedPosts = [{id: 1, title: 'title1',.....}, [...] ...]
// boardPosts used in MyBoard component
boardPosts = [{id: 1, title: 'title1',.....}, [...] ...]
So, I wonder if the following problem occurred because the key values of MyScrap component and MyBoard component overlap due to the following structure on one page or if there is another reason.
Try using rowKey in List component. You can check the example here
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
rowKey={(item) => item.id}
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};
I ended up pulling off what I wanted. However, it's giving me an array of the state instead of rendering each one separately. This is probably very simple and I'm more than likely over-complicating it but hey, any help would be nice.
Here's what I currently am dealing with
And here's a better example: https://i.imgur.com/WLDkbOb.gif
And lastly here's probably the best overview: https://imgur.com/a/zintqTA
constructor(props) {
super(props);
this.state = {
data: [],
loading: false,
}
}
ws = new WebSocket(URL)
componentDidMount() {
this.ws.onopen = () => {
console.log('connected')
}
this.ws.onmessage = e => {
const tbox = JSON.parse(e.data);
if(tbox.data && tbox.data.length > 0){
this.setState({
data : this.state.data.concat(tbox.data[0]),
})
}
}
this.ws.onclose = () => {
console.log('disconnected')
this.setState({
ws: new WebSocket(URL),
})
}
}
render() {
let { data } = this.state;
const chatBox = data.map(item => {
return (
<List
key={item.id}
dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user} {item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>
)
})
return (
<div>
<div>
{chatBox}
</div>
I'm trying to loop through the state and render each message separately
I think you don't need to loop through this.state.data[] because you are already setting data source to antd <List> component. antd List component handles collection of objects for us.
This would be the code for rendring your this.state.data:
const chatBox = <List dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user}
{item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>;
you can have a look at these links :
https://stackblitz.com/run
https://ant.design/components/list/
I have a small app fetching Movies. The component tree isn't very deep. I have state in App.js and it passes data to the Movies.js component. Now, Movies.js is just a container to render the ul list of Movies.
App.js passing data to Movies
<Movies
modalData={this.state.modalData}
toggleModal={this.openModal}
isOpen={this.state.modalIsOpen}
closeModal={this.closeModal}
items={this.state.data}
getMovieInfo={this.getMovieInfo}
addToFavorites={this.addToFavorites}
/>
Movies.js
const Movies = props => {
const { items, getMovieInfo, addToFavorites, isOpen, toggleModal, closeModal, modalData } = props;
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
imdbID={item.imdbID}
title={item.Title}
poster={item.Poster}
getMovieInfo={getMovieInfo}
addToFavorites={addToFavorites}
imdbid={item.imdbID}
isOpen={isOpen}
toggleModal={toggleModal}
closeModal={closeModal}
modalData={modalData}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
and finally Movies.js which is just an item that is rendered in the Movie component. I tried using destructuring to keep the items prop for the Movies component because it needs that to map over and then ...props to Movie. I cant seem to make this work.
My app works fine but this is 'prop-drilling' and I think context may not be appropriate because this is not a deep tree. Higher Order Components, I don't understand yet. What can I do to make this better?
Movie.js
const Movie = props => {
const {
id,
imdbID,
title,
poster,
getMovieInfo,
addToFavorites,
isOpen,
toggleModal,
closeModal,
modalData,
} = props;
return (
<React.Fragment>
<li className="movie" id={id}>
<img srcSet={poster} alt={title} imdbid={imdbID} />
<div className="movie-title">{title}</div>
<div className="button-container">
<button
type="submit"
className="button-small"
onClick={toggleModal}
imdbid={imdbID}
title={title}
id={id}>
{' '}
Info!!
</button>
<button
type="submit"
className="button-small"
onClick={addToFavorites}
imdbid={imdbID}
id={id}>
{' '}
Fav
</button>
</div>
</li>
<Modal className="modal" selector="#modal-root" isOpen={isOpen} onClick={closeModal}>
<div className="modal-1">
You can do like:
const Movies = ({ items, ...other}) => {
<Movie {...other} />
Or,
const { items, ...other } = props
<Movie {...other} />
Your code in example:
const Movies = ({ items, ...other}) => {
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
{...other}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
Your update made me afraid. You need to destructure all otherwise it will go even longer like id={other.id}. If you feel it's good rather, then do:
const { items, ...other } = props
// now when you use other props like imdbid
imdbid={other.imdbid}
I am trying to update the list when my redux store changes but for some odd reason it isn't. I have to manually refresh the page to see my changes. Here's the snippet of my List component and rowRenderer.
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={visibleRequest.length}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref={registerChild}
className="List"
height={height}
rowHeight={listRowHeight}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
width={width}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
_rowRenderer = ({ index, key, style }) => {
const { loadedRowsMap, selected } = this.state;
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
} else {
content = (
<div className="placeholder" style={{ width: _.random(100, 200) }} />
);
}
return (
<PendingChat
key={key}
content={content}
style={style}
row={row}
{...this.props}
/>
);
};
Yeah, I ran into the same problem. Its because the references to your objects don't change when you do
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
}
Take a look at immutability.
I appear to have a decent understanding of this principal, which allows me to get by, until now. I am applying a key prop to all children of all iterators, and yet I'm still getting this warning.
A FacilitiesContainer is rendering a FacilitiesComponent, which in turn renders a list of Facilities, which renders a list of Courses. A Course does not use an iterator. However, the FacilitiesContainer is passing the FacilitiesComponent through a HOC, which is returning the final component. There's nothing in the HOC that modifies the passed components, so I'm not sure if this is a problem.
// The render method of FacilitiesContainer
render = () => {
let FacilitiesWithSearch = SearchHOC(
BasicSearch,
FacilitiesComponent,
{data: this.state.facilities }
);
return <FacilitiesWithSearch />;
}
class FacilitiesComponent extends Component {
renderFacilities = () => (
this.props.data.map((facilityData, index) =>
<Facility key={index} data={facilityData} />
)
)
render = () => (
<Grid>
<Row>
<Col xs={12} sm={8} smOffset={2} md={8} mdOffset={1}>
{
this.props.data.length > 0
? this.renderFacilities()
: <div>No results</div>
}
</Col>
</Row>
</Grid>
)
}
const Facility = ({ data }) => (
<Panel>
<Panel.Heading>
<Panel.Title>{data.Name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<Grid>
<Row>
<p><b>Address:</b><br />
{data.Street}<br />
{data.City}, {data.State} {data.Zip}
</p>
<p><b>Phone:</b> {data.Phone}</p>
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
</Row>
</Grid>
</Panel.Body>
</Panel>
);
You indeed didn't provide keys to p elements here:
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
Should be
{
data.Courses.map((courseData, index) =>
<p key={index}><Course data={courseData} /></p>)
}
Try to append a string to the index before assigning it to the key. That's because you are only using index (0,1,2...) both for your list of facilities and list of courses, so there will be duplicated indexes in the final rendered component. If you do as below you ensure that each index is unique:
<Facility key={`facility_${index}`} data={facilityData} />
and
<Course key={`course_${index}`} data={courseData} />