I am trying to create Tabs and have JSX Components dynamically placed into each Tab as content. I am using React and Polaris as I am creating a new Shopify App.
I cannot seem to work out how to do this - I am very new to Javascript/Typescript and even React.
I have all the Tabs working showing the correct details in each, but I cannot pull the child JSX 'DesignForm' and make it show as within the First Tab.
import React, { Children } from "react";
import { Card, Page, Layout, TextContainer, Image, Stack, Link, Heading, Tabs} from "#shopify/polaris";
import {ReactNode, useState, useCallback} from 'react';
import { DesignForm } from "../designform/DesignForm";
export function NavTabs() {
const [selected, setSelected] = useState(0);
interface childrenProps {
children: JSX.Element;
}
const index = ({ children }: childrenProps) => {
return (
<>
<DesignForm />
{children}
</>
);
};
const handleTabChange = useCallback(
(selectedTabIndex) => setSelected(selectedTabIndex),
[],
);
const tabs = [
{
id: 'all-customers-4',
content: 'All',
accessibilityLabel: 'All customers',
panelID: 'all-customers-content-4',
children: DesignForm,
},
{
id: 'accepts-marketing-4',
content: 'Accepts marketing',
panelID: 'accepts-marketing-content-4',
},
{
id: 'repeat-customers-4',
content: 'Repeat customers',
panelID: 'repeat-customers-content-4',
},
{
id: 'prospects-4',
content: 'Prospects',
panelID: 'prospects-content-4',
},
];
return (
<Card>
<Tabs
tabs={tabs}
selected={selected}
onSelect={handleTabChange}
disclosureText="More views"
>
<Card.Section title={tabs[selected].content}>
<p>Tab {selected} selected</p>
</Card.Section>
<Card.Section children={tabs[selected].children}></Card.Section>
</Tabs>
</Card>
);
}
in
{
id: 'all-customers-4',
content: 'All',
accessibilityLabel: 'All customers',
panelID: 'all-customers-content-4',
children: DesignForm,
}
Your children (i.e DesignForm) is a function here you should set the children to your component instead
{
id: 'all-customers-4',
content: 'All',
accessibilityLabel: 'All customers',
panelID: 'all-customers-content-4',
children: <DesignForm/>,
}
You also could replace
<Card.Section children={tabs[selected].children}></Card.Section>
by
<Card.Section>
{tabs[selected].children}
</Card.Section>
Few pointers
Move this outside of the component for performance reasons, and React components should start with Capital letter
interface childrenProps {
children: JSX.Element;
}
// change from index to Index
const Index = ({ children }: childrenProps) => {
return (
<>
<DesignForm />
{children}
</>
);
};
If this is static move it outside the component, as it will be recreated in every render
const tabs = [
{
id: 'all-customers-4',
content: 'All',
accessibilityLabel: 'All customers',
panelID: 'all-customers-content-4',
},
{
id: 'accepts-marketing-4',
content: 'Accepts marketing',
panelID: 'accepts-marketing-content-4',
},
{
id: 'repeat-customers-4',
content: 'Repeat customers',
panelID: 'repeat-customers-content-4',
},
{
id: 'prospects-4',
content: 'Prospects',
panelID: 'prospects-content-4',
},
];
Create a component map, or something similar to this
const componentMap = {
['all-customers-4']: DesignForm,
// ... more components can be added in the future
}
const EmptyComponent = () => null;
export function NavTabs() {
const [selected, setSelected] = useState(0);
const handleTabChange = useCallback(
(selectedTabIndex) => setSelected(selectedTabIndex),
[],
);
const ChildComponent = useMemo(() => {
return componentMap[selected] ?? EmptyComponent
}, [selected])
return (
<Card>
<Tabs
tabs={tabs}
selected={selected}
onSelect={handleTabChange}
disclosureText="More views"
>
<Card.Section title={tabs[selected].content}>
<p>Tab {selected} selected</p>
<ChildComponent />
</Card.Section>
<Card.Section children={tabs[selected].children}></Card.Section>
</Tabs>
</Card>
);
}
Hope this helps you in some way to find a good solution
Cheers
Related
On every click react rerender every item. How to avoid it? I want react to only render items that have changed. I tried using react memo and usecallback but it didn't help. I can't understand what is the reason. What are the ways to solve this problem? Thank you.
App.ts
import React, { useState } from "react";
import "./styles.css";
import ListItem from "./ListItem";
const items = [
{ id: 1, text: "items1" },
{ id: 2, text: "items2" },
{ id: 3, text: "items3" },
{ id: 4, text: "items4" },
{ id: 5, text: "items5" },
{ id: 6, text: "items6" },
{ id: 7, text: "items7" }
];
export default function App() {
const [activeIndex, setActiveIndex] = useState(1);
const onClick = (newActiveIndex: number) => {
setActiveIndex(newActiveIndex);
};
return (
<div className="App">
{items.map(({ id, text }) => (
<ListItem
key={id}
id={id}
text={text}
activeId={activeIndex}
onClick={onClick}
/>
))}
</div>
);
}
Item.ts
import React from "react";
interface ListItemProps {
id: number;
activeId: number;
text: string;
onClick: (newActiveIndex: number) => void;
}
const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
const isActive = activeId === id;
const style = {
marginRight: "42px",
marginTop: "24px",
color: isActive ? "green" : "red"
};
console.log(`update id: ${id}`);
return (
<div>
<span style={style}>id: {id}</span>
<span style={style}>is active: {isActive ? "active" : "inactive"}</span>
<span style={style}>text: {text}</span>
<button onClick={() => onClick(id)}>setActive</button>
</div>
);
};
export default ListItem;
activeIndex changes with each click, so all of the components have new prop values and will need to re-render.
Instead of passing the activeIndex to every component every time:
const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
const isActive = activeId === id;
Pass an isActive to each component:
const ListItem: React.FC<ListItemProps> = ({ id, isActive, text, onClick }) => {
Effectively moving the calculation of the bool from the component to the consuming code:
<ListItem
key={id}
id={id}
text={text}
isActive={activeIndex === id}
onClick={onClick}
/>
Then memoization (coupled with useCallback for the onClick handler) should work because most components (the ones which aren't changing their "active" state) won't receive new props and won't need to re-render.
Even with memoization it won't work, because your onClick function changes your components state, and your children components depend on activeIndex value, so every time you click you change the components state, and when state changes, the component re-renders itself and it's children, if your children didn't depend on your state, they wouldn't re-render (if you use memoization).
Plus to David's answer i would return ListItem in React.memo React.memo(ListItem)
I am trying to create an ObjectList component, which would contain a list of Children.
const MyList = ({childObjects}) => {
[objects, setObjects] = useState(childObjects)
...
return (
<div>
{childObjects.map((obj, idx) => (
<ListChild
obj={obj}
key={idx}
collapsed={false}
/>
))}
</div>
)
}
export default MyList
Each Child has a collapsed property, which toggles its visibility. I am trying to have a Collapse All button on a parent level which will toggle the collapsed property of all of its children. However, it must only change their prop once, without binding them all to the same state. I was thinking of having a list of refs, one for each child and to enumerate over it, but not sure if it is a sound idea from design perspective.
How can I reference a dynamic list of child components and manage their state?
Alternatively, is there a better approach to my problem?
I am new to react, probably there is a better way, but the code below does what you explained, I used only 1 state to control all the objects and another state to control if all are collapsed.
Index.jsx
import MyList from "./MyList";
function Index() {
const objList = [
{ data: "Obj 1", id: 1, collapsed: false },
{ data: "Obj 2", id: 2, collapsed: false },
{ data: "Obj 3", id: 3, collapsed: false },
{ data: "Obj 4", id: 4, collapsed: false },
{ data: "Obj 5", id: 5, collapsed: false },
{ data: "Obj 6", id: 6, collapsed: false },
];
return <MyList childObjects={objList}></MyList>;
}
export default Index;
MyList.jsx
import { useState } from "react";
import ListChild from "./ListChild";
const MyList = ({ childObjects }) => {
const [objects, setObjects] = useState(childObjects);
const [allCollapsed, setallCollapsed] = useState(false);
const handleCollapseAll = () => {
allCollapsed = !allCollapsed;
for (const obj of objects) {
obj.collapsed = allCollapsed;
}
setallCollapsed(allCollapsed);
setObjects([...objects]);
};
return (
<div>
<button onClick={handleCollapseAll}>Collapse All</button>
<br />
<br />
{objects.map((obj) => {
return (
<ListChild
obj={obj.data}
id={obj.id}
key={obj.id}
collapsed={obj.collapsed}
state={objects}
setState={setObjects}
/>
);
})}
</div>
);
};
export default MyList;
ListChild.jsx
function ListChild(props) {
const { obj, id, collapsed, state, setState } = props;
const handleCollapse = (id) => {
console.log("ID", id);
for (const obj of state) {
if (obj.id == id) {
obj.collapsed = !obj.collapsed;
}
}
setState([...state]);
};
return (
<div>
{obj} {collapsed ? "COLLAPSED!" : ""}
<button
onClick={() => {
handleCollapse(id);
}}
>
Collapse This
</button>
</div>
);
}
export default ListChild;
I have some nested data that needs to generate a form of checkboxes dynamically. The "Tasks" data, needs a parent checkbox, as per MaterialUI's docs under "Indeterminate" in their Checkboxes example . I'm struggling to understand how to apply their example in conjunction with my code.
Current data used to generate dynamic checkboxes:
const availableFilters = useMemo(
() => [
{
title: "Status",
filterOptions: [
{ label: "Ready for Review"},
{ label: "Ready for Techcheck"},
],
},
{
title: "Offices",
filterOptions:
[
{ label: "London" },
{ label: "Berlin"},
}],
},
{
title: "Tasks",
filterGroups:
[
{
title: "3D"
filterOptions: [
{label: "Animation"},
{label: "Lighting"},
],
},
{
title: "Comp"
filterOptions: [
{label: "Compositing"},
{label: "Prep"}
],
},
],
},
{
title: "Creator",
filterOptions: [{ label: "Alex"}, { label: "John"}],
},
],
[filterInfo, taskGroups]
);
Getting confused rather easily with the nesting and some recursive typescript stuff.
This is the handlerFunction in the parent component(AddtoReviewMenu) with "Lodash":
const [checkedValues, setCheckedValues] = useState<{[key: string]: string[];}>({});
const handleCheckboxChange = useCallback(
(checked: boolean, title: string, value: string) => {
if(checked) {
if (Object.keys(checkedValues).includes(title)) {
setCheckedValues({
...checkedValues,
[title]: [...checkedValues[title], value],
});
} else {
setCheckedValues({
...checkedValues,
[title]: [value],
});
} else {
setCheckedValues({
...checkedValues,
[title]: _(checkedValues[title])
.filter((c) => c !== value)
.value(),
});
}
},
[checkedValues]
);
Here is the child component that populates the the checkboxes based on the data:
import React from "react";
import IconButton, { IconButtonProps } from "#mui/material/IconButton";
import Box, { FormControl, Stack } from "#mui/material/";
interface ExpandMoreProps extends IconButtonProps {
expand: Boolean;
}
const ExpandMore = styled((props: ExpandMoreProps) => {
const { expand, ...other } = props;
return <IconButton {...other} />;
});
interface Props extends FilterGroup {
handleCheckboxChange: (
checked: boolean,
title: string,
value: string
) => void;
}
export default function FilterOptionGroup(props: Props) {
const { filterGroups, title, handleCheckboxChange } = props;
const [expanded, setExpanded] = useState(true);
const handleExpandClick = () => {
setExpanded(!expanded);
};
return (
<Box>
<FormControl>
<Stack>
<ExpandMore expand={expanded} onClick={handleExpandClick}>
<ArrowUpIcon />
</ExpandMore>
<FormLabel> {title} </FormLabel>
</Stack>
<Collapse in={expanded}>
{filterOptions
? filterOptions?.map((item) => (
<FormControlLabel
control={
<Checkbox
onChange={(event, checked) =>
handleCheckboxChange(checked, title, item.label)
}
/>
}
label={item.label}
value={item.label}
/>
))
: filterGroups?.map((item) => (
<FilterOptionGroup
title={item.title}
filterOptions={item.filterOptions}
filterGroups={item.filterGroups}
handleCheckboxChange={handleCheckboxChange}
/>
))}
</Collapse>
</FormControl>
</Box>
);
}
And a "Types.ts" file:
export interface FilterGroup {
title: string;
filterOptions?: FilterOption[];
filterGroups?: FilterGroup[];
}
export interface FilterOption {
label: string;
}
So I am a beginner in react. I was messing around with a to-do list and encountered a state related problem.
//main todolist
import React from 'react'
import './todolist.css'
import Itemtodo from './Itemtodo'
class Todolist extends React.Component{
constructor(){
super()
this.state = {
todos: [
{
id:1,
text: 'html',
completed: true
},
{
id:2,
text: 'css',
completed: true
},
{
id:3,
text: 'js',
completed: false
},
{
id:4,
text: 'react',
completed: false
},
{
id:5,
text: 'review',
completed: false
}
]
}
}
// i think it came from this method
ev = (id) =>{
this.setState((prev) =>{
const newarr = prev.todos.map((data) =>{
if(data.id === id){
data.completed = !data.completed
}
return data
})
return {
todos:newarr
}
})
}
render(){
const array = this.state.todos.map(data => <Itemtodo key={data.id} todo={data.text} ic={data.completed} ev={this.ev} id={data.id}/>)
return (
<div className="todolist">
{array}
</div>
)
}
}
export default Todolist
// item todo
import React, { Component } from 'react'
import './itemtodo.css'
export class Itemtodo extends Component {
render() {
return (
<div className ='itemtodo'>
<input type='checkbox' checked={this.props.ic} onChange={() => this.props.ev(this.props.id)}/>
<p>{this.props.todo}</p>
</div>
)
}
}
export default Itemtodo
I REALLY think that the problem was from "main todolist" because if I changed the "ev" method to do "checked every checkbox with a click" like this, it worked
// i think it came from this method
ev = (id) =>{
this.setState((prev) =>{
const newarr = prev.todos.map((data) =>{
data.completed = true
return data
})
return {
todos:newarr
}
}) //set state ends
}
I did some experiment by console loging the "newar" and it did not change. So I think it's because of the
data.completed = !data.completed
did not work please help me! Thank you
Hi Please check this example. It is working fine.
Todolist Component
import React, {Component} from 'react'
class Todolist extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [
{id: 1, text: 'html', completed: true},
{id: 2, text: 'css', completed: true},
{id: 3, text: 'js', completed: false},
{id: 4, text: 'react', completed: false},
{id: 5, text: 'review', completed: false}
]
}
}
// i think it came from this method
ev = (id, changedValue) => {
this.setState((prev) => {
let item = prev.todos.filter((data) => data.id === id)[0];
item.completed = changedValue;
return{
todos: prev.todos
};
})
};
render() {
const array = this.state.todos.map(data => <Itemtodo key={data.id} todo={data.text} ic={data.completed}
ev={this.ev} id={data.id}/>);
return (
<div className="todolist">
{array}
</div>
)
}
}
export default Todolist
Itemtodo Component
class Itemtodo extends Component {
render() {
return (
<div className='itemtodo'>
<input type='checkbox' checked={this.props.ic} onChange={() => this.props.ev(this.props.id, !this.props.ic)}/>
{this.props.todo}
</div>
)
}
}
Here is a working example of a todo list that has TodoItem as a pure component, because toggleCompleted will not mutate this will work (you have to click on the todo to toggle completed).
//using React.memo will make TodoItem a pure
// component and won't re render if props didn't change
const TodoItem = React.memo(function TodoItem({
todo: { id, name, completed },
toggleCompleted,
}) {
const r = React.useRef(0);
r.current++;
return (
<li
onClick={toggleCompleted(id)}
style={{ cursor: 'pointer' }}
>
rendered:{r.current}, {name}, completed:
{completed.toString()}
</li>
);
});
function TodoList({ todos, toggleCompleted }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
toggleCompleted={toggleCompleted}
/>
))}
</ul>
);
}
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, name: '1', completed: false },
{ id: 2, name: '2', completed: false },
]);
//use callback so the handler never changes
const toggleCompleted = React.useCallback(
(id) => () =>
setTodos((todos) =>
//map todos into a new array of todos
todos.map(
(todo) =>
todo.id === id
? //shallow copy todo and toggle completed
{ ...todo, completed: !todo.completed }
: todo //just return the todo
)
),
[]
);
return (
<TodoList
todos={todos}
toggleCompleted={toggleCompleted}
/>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
And here is a broken example where I will mutate the todo, even though you changed the data you did not change the reference of the todo item so React won't re render pure TodoItem component.
//using React.memo will make TodoItem a pure
// component and won't re render if props didn't change
const TodoItem = React.memo(function TodoItem({
todo: { id, name, completed },
toggleCompleted,
}) {
const r = React.useRef(0);
r.current++;
return (
<li
onClick={toggleCompleted(id)}
style={{ cursor: 'pointer' }}
>
rendered:{r.current}, {name}, completed:
{completed.toString()}
</li>
);
});
function TodoList({ todos, toggleCompleted }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
toggleCompleted={toggleCompleted}
/>
))}
</ul>
);
}
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, name: '1', completed: false },
{ id: 2, name: '2', completed: false },
]);
//use callback so the handler never changes
const toggleCompleted = React.useCallback(
(id) => () =>
setTodos((todos) =>
//map todos into a new array of todos
todos.map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
console.log('changed todo:', todo);
}
//it is always returning the same todo that it
// got passed into, only mutates the one that
// needs to be toggled but that todo is still the
// same
return todo;
})
),
[]
);
return (
<TodoList
todos={todos}
toggleCompleted={toggleCompleted}
/>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I'm working on a React.js based outliner (in similar vein to workflowy). I'm stuck at a point. When I press enter on any one item, I want to insert another item just below it.
So I have used setState function to insert an item just below the current item as shown by the code snippet below:
this.setState({
items: this.state.items.splice(this.state.items.map((item) => item._id).indexOf(itemID) + 1, 0, {_id: (new Date().getTime()), content: 'Some Note'})
})
However, the app is re-rendering as blank without any error showing up.
My full source so far:
import './App.css';
import React, { Component } from 'react';
import ContentEditable from 'react-contenteditable';
const items = [
{
_id: 0,
content: 'Item 1 something',
note: 'Some note for item 1'
},
{
_id: 5,
content: 'Item 1.1 something',
note: 'Some note for item 1.1'
},
{
_id: 1,
content: 'Item 2 something',
note: 'Some note for item 2',
subItems: [
{
_id: 2,
content: 'Sub Item 1 something',
subItems: [{
_id: 3,
content: 'Sub Sub Item 4'
}]
}
]
}
];
class App extends Component {
render() {
return (
<div className="App">
<Page items={items} />
</div>
);
}
}
class Page extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items
};
this.insertItem = this.insertItem.bind(this);
}
render() {
return (
<div className="page">
{
this.state.items.map(item =>
<Item _id={item._id} content={item.content} note={item.note} subItems={item.subItems} insertItem={this.insertItem} />
)
}
</div>
);
}
insertItem(itemID) {
console.log(this.state.items);
this.setState({
items: this.state.items.splice(this.state.items.map((item) => item._id).indexOf(itemID) + 1, 0, {_id: (new Date().getTime()), content: 'Some Note'})
})
console.log(this.state.items);
}
}
class Item extends Component {
constructor(props) {
super(props);
this.state = {
content: this.props.content,
note: this.props.note
};
this.saveItem = this.saveItem.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
render() {
return (
<div key={this.props._id} className="item">
<ContentEditable html={this.state.content} disabled={false} onChange={this.saveItem} onKeyPress={(event) => this.handleKeyPress(this.props._id, event)} />
<div className="note">{this.state.note}</div>
{
this.props.subItems &&
this.props.subItems.map(item =>
<Item _id={item._id} content={item.content} note={item.note} subItems={item.subItems} insertItem={this.insertItem} />
)
}
</div>
);
}
saveItem(event) {
this.setState({
content: event.target.value
});
}
handleKeyPress(itemID, event) {
if (event.key === 'Enter') {
event.preventDefault();
console.log(itemID);
console.log(event.key);
this.props.insertItem(itemID);
}
}
}
export default App;
The github repo: https://github.com/Hirvesh/mneme
Can anybody help me understand as to why it's not rendering again after I update the items?
Edit:
I have update the code to add keys, as suggested below, still rendering as blank, despite the state updating:
import './App.css';
import React, { Component } from 'react';
import ContentEditable from 'react-contenteditable';
const items = [
{
_id: 0,
content: 'Item 1 something',
note: 'Some note for item 1'
},
{
_id: 5,
content: 'Item 1.1 something',
note: 'Some note for item 1.1'
},
{
_id: 1,
content: 'Item 2 something',
note: 'Some note for item 2',
subItems: [
{
_id: 2,
content: 'Sub Item 1 something',
subItems: [{
_id: 3,
content: 'Sub Sub Item 4'
}]
}
]
}
];
class App extends Component {
render() {
return (
<div className="App">
<Page items={items} />
</div>
);
}
}
class Page extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items
};
this.insertItem = this.insertItem.bind(this);
}
render() {
return (
<div className="page">
{
this.state.items.map(item =>
<Item _id={item._id} key={item._id} content={item.content} note={item.note} subItems={item.subItems} insertItem={this.insertItem} />
)
}
</div>
);
}
insertItem(itemID) {
console.log(this.state.items);
this.setState({
items: this.state.items.splice(this.state.items.map((item) => item._id).indexOf(itemID) + 1, 0, {_id: (new Date().getTime()), content: 'Some Note'})
})
console.log(this.state.items);
}
}
class Item extends Component {
constructor(props) {
super(props);
this.state = {
content: this.props.content,
note: this.props.note
};
this.saveItem = this.saveItem.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
render() {
return (
<div className="item">
<ContentEditable html={this.state.content} disabled={false} onChange={this.saveItem} onKeyPress={(event) => this.handleKeyPress(this.props._id, event)} />
<div className="note">{this.state.note}</div>
{
this.props.subItems &&
this.props.subItems.map(item =>
<Item _id={item._id} key={item._id} content={item.content} note={item.note} subItems={item.subItems} insertItem={this.insertItem} />
)
}
</div>
);
}
saveItem(event) {
this.setState({
content: event.target.value
});
}
handleKeyPress(itemID, event) {
if (event.key === 'Enter') {
event.preventDefault();
console.log(itemID);
console.log(event.key);
this.props.insertItem(itemID);
}
}
}
export default App;
Edit 2:
As per the suggestions below, added key={i._id}, still not working, pushed latest code to github if anybody wants to take a look.
this.state.items.map((item, i) =>
<Item _id={item._id} key={i._id} content={item.content} note={item.note} subItems={item.subItems} insertItem={this.insertItem} />
)
You miss to put a key on Item, because of that React could not identify if your state change.
<div className="page">
{
this.state.items.map((item, i) =>
<Item _id={item._id}
key={i} // <---- HERE!!
content={item.content}
note={item.note}
subItems={item.subItems}
insertItem={this.insertItem} />
)}
</div>
There are multiple issues with your code:
in you are passing this.insertItem into SubItems (which does not exists)
your this.state.items.splice, changes this.state.items, but returns []. So your new State is []
try this:
this.state.items.splice(/* your splice argument here */)
this.setState({ items: this.state.items }, () => {
// this.setState is async! so this is the proper way to check it.
console.log(this.state.items);
});
To accomplish what you actually want, this will not be enough.. But at least, when you have fixed it, you can see some results.