I have list whith several items. Each item contains service information and button.
Each tender contains id and other props which I want to get after click
How can i get specific item properties after click on the button?
UPD:
export default class ListFresh extends React.Component {
constructor(props) {
super(props);
this.state = { tenders: [] };
}
componentDidMount() {
elastic.search().catch(e => alert(e)).then(
data => this.setState({tenders: data.items})
);
}
render() {
return (
<TenderList tenders={this.state.tenders}/>
);
}
}
class TenderList extends React.Component {
render() {
return (
<div className="tender-list">
<List items={this.props.tenders}/>
</div>
);
}
}
Generate tender
export default class List extends React.Component {
constructor(props) {
super(props);
this.state = { nodes: [] };
}
render () {
const data = this.props.items;
var node;
data.map(item => (
console.log(item),
node = {
headerName : item._source.tender.title,
isOpened: false,
isReactComponent: true,
items : [
<Tender tender={item._source} id={item._id}/>
],
height: 250
},
DATALIST.push(node)
));
return (
<div id="admin-menu">
<ReactExpandableListView
data={DATALIST}
headerAttName="headerName"
itemsAttName="items"
/>
</div>
)
}
}
Related
so I was working on a basic Todo app using React.js and I was wondering why the todo component does not automatically re-render once the state changed (the state contains the list of todos- so adding a new todo would update this array)? It is supposed to re-render the Header and the Todo component of the page with the updated array of todos passed in as props. Here is my code:
import React from 'react';
import './App.css';
class Header extends React.Component {
render() {
let numTodos = this.props.todos.length;
return <h1>{`You have ${numTodos} todos`}</h1>
}
}
class Todos extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => {
return (<Todo index={index} todo={todo} />)
})
}
</ul>
)
}
}
class Todo extends React.Component {
render() {
return <li key={this.props.index}>{this.props.todo}</li>
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.addnewTodo = this.addnewTodo.bind(this);
}
addnewTodo = () => {
let inputBox = document.getElementById("input-box");
if (inputBox.value === '') {
return;
}
this.props.handleAdd(inputBox.value);
}
render() {
return (
<div>
<input id="input-box" type="text"></input>
<button type="submit" onClick={this.addnewTodo}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: ['task 1', 'task 2', 'task 3']}
this.handleNewTodo = this.handleNewTodo.bind(this);
}
handleNewTodo(todo) {
let tempList = this.state.todos;
tempList.push(todo);
this.setState = { todos: tempList };
}
render() {
return (
<div>
<Header todos={this.state.todos} />
<Todos todos={this.state.todos} />
<Form todos={this.state.todos} handleAdd={this.handleNewTodo} />
</div>
)
}
}
You are not updating the state correctly.
You need to make a copy of the this.state.todos, add the new todo in the copied array and then call this.setState function
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
Notice that this.setState is a function
You're updating state incorrectly,
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
This is the correct syntax.
I have a basic 'wrapper' component which contains child 'item' components
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
render() {
return (
<div>Items count- {this.state.items.length}
{this.state.items.map(function (item, i) {
<Item itemId={item.itemId} />
})}
</div>
);
}
}
class Item extends React.Component {
constructor(props) { super(props); }
render() {
return (
<div class="item">{this.props.itemId}</div>
);
}
}
Do I call setState({ "items":[{ "itemId": 22 }] }); to update items in UI?
Want to add/remove 'item' and get UI updated accordingly.
For updates, you want to do something like the following...
// Update item
this.setState({ "items":this.state.items.map(function(item) {
if (item.itemId !== 22) return item;
// update item here
// remember to return item
return item
})
});
// Remove item
this.setState({ "items":this.state.items.filter(item => {
return item.itemId !== 22
})
})
// Add item
this.setState({ "items": this.state.items.concat(newItem)
})
I suggest putting these into React class methods though.
import React from 'react';
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this)
this.removeItem = this.removeItem.bind(this)
this.updateItem = this.updateItem.bind(this)
}
addItem (item) {
this.setState({
items: this.state.items.concat(item)
})
}
updateItem(id, updatedItem) {
this.setState({
items: this.state.items.map(function (item) {
if (item.itemId !== id) return item;
return updatedItem;
})
})
}
removeItem(id) {
this.setState({
items: this.state.items.filter(function(item) {
return item.itemId !== id
})
})
}
render() {
return (
<div>Items count- {this.state.items.length}
{this.state.items.map(function (item, i) {
<Item itemId={item.itemId} />
})}
</div>
);
}
}
class Item extends React.Component {
constructor(props) { super(props); }
render() {
return (
<div class="item">{this.props.itemId}</div>
);
}
}
State is not mutable, so the code you've shown there will replace items with an array with one object. If you'd like to add/remove from the array, you'll first need to copy the array somehow , and replace with the new one. You should use the function argument of setState for that. Ex:
this.setState(function (currentState) {
return {items: currentState.concat({itemId: 22})}
});
This is how you add and remove to and from the state items array:
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
addItems = (id) => {
// copies all current items and adds a new one
this.setState({items: [...this.state.items, {itemId: id}]
})
}
removeItems = (id) => {
const newItemList = this.state.items.filter((item) => item.itemId !== id)
this.setState({items: newItemList
})
}
render() {
return (
<div>
<div>Items count - {this.state.items.length}
<button onClick={() => this.addItems(this.state.items.length + 1)}>Add Item</button>
</div>
{
this.state.items.map((item) => {
return (
<Item key={item.itemId} itemId={item.itemId} removeItems={this.removeItems} />
)
})
}
</div>
);
}
}
ReactDOM.render(<Wrapper />, document.getElementById('root'))
class Item extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div className="item">test{this.props.itemId} <button onClick={() => this.props.removeItems(this.props.itemId)}>Remove Item</button></div>;
}
}
<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>
I'm trying to pass an object from one sibling to another via a parent.
Sibling one contains a page list:
this.state = {pages: [
{pageTitle: 'Home', pageDesc:'', items: [], id:''},
{pageTitle: 'About', pageDesc:'', items: [], id:''},
{pageTitle: 'Contact', pageDesc:'', items: [], id:''},
{pageTitle: 'Contact', pageDesc:'', items: [], id:''}
]}
and a function to receive data from the parent
update = () => {
let newPageList = [...this.state.pages, this.props.addToList];
this.setState({pages: newPageList})
}
Sibling two contains items that need to be added to the above page list when a button is pressed:
this.state = {
pageTitle: 'New page', pageDesc:'New desc', items: [], id:''
}
And a function which passes it's state to the parent:
addNewPage = () => {
let info = {...this.state};
this.props.callBack(info)
}
Here's the parent component
constructor(props){
super(props);
this.state={
data: {
pageTitle: '', pageDesc:'', items: [], id:''
}
}
}
updatePageList = (pageAdded) =>{
this.setState({data:pageAdded});
console.log(this.state)
}
render(){
return(
<div>
<PageBuilder callBack={this.updatePageList} />
<PageList addToList={this.state.data} />
</div>
)
}
I don't know what your exact problem is but I've built an example showing that ChildOne adds a list which is then passed to ChildTwo via App as the parent, and then renders the list into view.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list: []
}
}
addToList = (data) => {
this.setState({ list: [...this.state.list, data] }, () => console.log('Parent\'s List (from callback):', this.state.list.join(' ')))
console.log('Parent\'s List (just after setState):', this.state.list.join(' '))
}
componentDidUpdate() {
console.log('Parent\'s List (componentDidUpdate):', this.state.list.join(' '))
}
render() {
return (
<div>
<ChildOne addItem={this.addToList}/>
<ChildTwo list={this.state.list}/>
</div>
)
}
}
class ChildOne extends React.Component {
constructor(props) {
super(props);
this.i = 0;
}
render() {
return (
<div>
<h1>ChildOne</h1>
<button onClick={() => this.props.addItem(this.i++)}>Add</button> {/*Note the arrow function inside onClick, it'll error without it*/}
</div>
)
}
}
class ChildTwo extends React.Component {
constructor(props) {
super(props);
this.state = {
mylist: props.list
}
}
//This one!
componentWillReceiveProps(props) {
this.setState({mylist: props.list}, () => console.log('Child Two\'s List (from callback):', this.state.mylist.join(' ')))
console.log('Child Two\'s List (just after setState):', this.state.mylist.join(' '))
}
componentDidUpdate() {
console.log('Child Two\'s List (componentDidUpdate):', this.state.mylist.join(' '))
}
render() {
return (
<div>
<h1>ChildTwo</h1>
{this.state.mylist.join(' ')}
</div>
)
}
}
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>
i have a problem - i need to change parent component's state from child component. I tried standard variant with props, but it's not helping in my case.
Here is code of Parent component:
class ThemesBlock extends React.Component {
constructor(props){
super(props);
this.state = {
currentThemeId: 0
}
}
changeState(n){
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
data.map(function (item) {
return <Theme key={item.id} data={item} changeState=
{item.changeState}/>
})
)
}
}
And here is my code for Child Component:
class Theme extends React.Component {
constructor(props){
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const { id, themename } = this.props.data;
const link = '#themespeakers' + id;
return (
<li><a href={link} onClick={() => this.changeState(id)}
className="theme">{themename}</a></li>
)
}
}
The primary issue is that changeState should be bound to the ThemesBlock instance, not to the Theme instance (by ThemesBlock).
Here's an example where I've bound it in the ThemesBlock constructor (I've also updated it to show what theme ID is selected):
class ThemesBlock extends React.Component {
constructor(props) {
super(props);
this.state = {
currentThemeId: 0
}
this.changeState = this.changeState.bind(this);
}
changeState(n) {
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
<div>
<div>
Current theme ID: {this.state.currentThemeId}
</div>
{data.map(item => {
return <Theme key={item.id} data={item} changeState={this.changeState} />
})}
</div>
)
}
}
class Theme extends React.Component {
constructor(props) {
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const {
data: {
id,
themename
},
changeState
} = this.props;
const link = '#themespeakers' + id;
return (
<li>{themename}</li>
)
}
}
const data = [
{id: 1, themename: "One"},
{id: 2, themename: "Two"},
{id: 3, themename: "Three"}
];
ReactDOM.render(
<ThemesBlock data={data} />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
Did you try to move map() before return? Like this:
render() {
let { data } = this.props;
const outputTheme = data.map(function (item) {
return <Theme key={item.id} data={item} changeState= {item.changeState}/>
})
return (
{outputTheme}
)
}
I am a React novice, so this might seem really simple, or maybe it isn't, I'm not sure. I'm building a basic to-do list. I want a mouseover effect on list items to pop up "delete this" text. But for my code so far, when I mouseover a list item, "delete this" pops up for all list items instead of just the one.
When I tried solving this by creating a new component for individual list items, that didn't seem to work. Any help is much appreciated!
class ToDosContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
heading: 'Something You Need To Do?',
todos: [
'wake up',
'brush your teeth'
],
}
this.addToDo = this.addToDo.bind(this)
}
addToDo(todo) {
this.setState((state) => ({
todos: state.todos.concat([todo])
}))
}
render() {
return (
<div>
<h1>To Do List</h1>
<h3>{this.state.heading}</h3>
<AddToDo addNew={this.addToDo} />
<ShowList tasks={this.state.todos} />
</div>
)
}
}
class AddToDo extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
this.updateNewToDo = this.updateNewToDo.bind(this)
this.handleAddToDo = this.handleAddToDo.bind(this)
}
//I think I can delete this part
updateNewToDo(e) {
this.setState({
newToDo: e.target.value
})
}
//
handleAddToDo() {
this.props.addNew(this.state.newToDo)
this.setState({
newToDo: ''
})
}
render() {
return (
<div>
<input
type="text"
value={this.state.newToDo}
onChange={this.updateNewToDo}
/>
<button onClick={this.handleAddToDo}> Add To Do </button>
</div>
)
}
}
class ShowList extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
}
onMouseOver(e) {
this.setState({
text: 'delete me'
})
console.log('hey')
}
onMouseOut(e) {
this.setState({
text: ''
})
console.log('hey hey')
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map((todo) => {
return <li onMouseEnter={this.onMouseOver.bind(this)} onMouseLeave={this.onMouseOut.bind(this)}> {todo} {text}</li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<ToDosContainer />, document.getElementById('helloworld'));
I would make a Task component. You don't want to set the state of the text in the ListView component, because then this.state.text is shared by each task in the map. Each task should be aware of its own hover, and not the hover of the other children.
class Task extends React.Component {
state = {
text: ""
};
onMouseOver(e) {
this.setState({
text: "delete me"
});
}
onMouseOut(e) {
this.setState({
text: ""
});
}
render() {
return (
<li
onMouseEnter={this.onMouseOver.bind(this)}
onMouseLeave={this.onMouseOut.bind(this)}
>
{this.props.todo} {this.state.text}
</li>
);
}
}
class ShowList extends React.Component {
constructor(props) {
super(props);
this.state = {
newToDo: ""
};
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map(todo => {
return <Task todo={todo} />;
})}
</ul>
</div>
);
}
}