React todo list, move from incompleted to completed list with information on the same li - reactjs

I am a beginner in react and need some help.
I have created a todo list which takes, the text,edit and remove button, a select menu a input box and a radio button. The list renders fine for the "in completed list".I am trying to move the following when the radio button is ticked to the "completed list". the text,the option on the select menu for that li aswell as the text for the last box (ticket number). At the moment this takes the todo item text and the last value selected for the select menu (regardless of the li selected). To make it clear this is a task list, the select menu will be a list of names that this task is assigned to, with a "ticket number" for that task. I need all this information on another array to be visual on the "completed list".
The "incompleted list" which is stored in the items state, only has a name. on clicking "complete" I need to pass the details from the select menu. How do i do this? At the moment i can pass the last edited select menu, but i need it to be specific to the li.
code can be found on https://codesandbox.io/s/9lj4mmon1o
import React, { Component } from 'react';
import Form from './Components/Form'
import './App.css';
import List from './Components/List'
import Completed from './Components/Completed'
class App extends Component {
constructor(props){
super(props)
this.state= {
isComplete:false,
isEditing:false,
text:"",
items:[],
completed:[]
}
this.submit=this.submit.bind(this);
this.eventHandler=this.eventHandler.bind(this)
}
submit=(e)=>{
e.preventDefault();
this.setState({
items:[
{
name:this.state.text,
},
...this.state.items
],
text:""
})
}
remove=(index)=>{
this.setState({
items:this.state.items.filter((_,i) => i!==index)
})
}
eventHandler=(e)=>{
this.setState ({
text:e.target.value
})
}
handleNameEdits=(e)=>{
this.setState({
text:e.target.value
})
}
myoptions=(option,indexToChange,name)=>{
this.setState({
webmaster:option
})
}
edit=(items,indexToChange)=>{
this.setState({
isEditing:!this.state.isEditing,
items:this.state.items.map((item,index)=>{
if(index== indexToChange){
return {name:this.state.text}
}
}),
text:""
})
}
onChange=(indexToChange,listName,)=>{
this.setState({
isComplete:!this.state.isComplete,
items:this.state.items.filter((_,i) => i!==indexToChange),
completed:[
{
name:listName.name,
webmaster:this.state.webmaster,
ticketId:this.state.ticketId
},
...this.state.completed
]
})
}
ticketId=(e)=>{
this.setState({
ticketId:e.target.value
})
}
render() {
return (
<div className="App">
<header className="App-header">
<Form submit={this.submit} myValue={this.state.text} eventHandler={this.eventHandler}/>
<h4 class="incompleted-title"> Incompleted Tasks </h4>
{this.state.items.map && this.state.items.map((item,index)=>(
<List key={index}
name={item.name}
edit={()=>this.edit(item,index)}
changeComplete={()=>this.onChange(index,item)}
remove={()=>this.remove(index) }
handleNameEdits={this.handleNameEdits}
myoptions={(e =>this.myoptions(e.target.value,index,item))}
isEditing={this.state.isEditing}
editText={this.state.changedtext}
ticketId={this.ticketId}
/>
))}
</header>
<div className="completed">
<h2>completed</h2>
{this.state.completed.length > 0 && (<Completed completed={this.state.completed}/>)}
</div>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Edit from './Edit';
class List extends Component {
constructor(props) {
super()
this.options = [
{name:'web1'},
{name:'web2'},
{name:'web3'},
{name:'web4'}
render() {
const {key} = this.props;
const x=this.options;
return (
<ul>
<li > <Edit handleNameEdits={this.props.handleNameEdits} isEditing={this.props.isEditing} editText={this.props.editText} /> <span className="li-text">{this.props.name}</span>
<div class="btn-group" role="group" aria-label="Basic example">
<button className='btn-style btn btn-info' onClick={this.props.edit}>{this.props.isEditing ? 'save':'edit'}</button>
<button className='btn btn-danger' onClick={this.props.remove}>Remove</button>
</div>
<select ref={this.props.Ref} class="select" onChange={this.props.myoptions}>{this.options.map(options => <option>{options.name}</option> )}
</select>
<label> Ticket Number </label><input type='text' onClick={this.props.ticketId} />
<label className='label'> Completed
</label><input type="checkbox" className="completedbox" onChange={this.props.changeComplete} />
</li>
</ul>
)
}
}
export default List;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Completed extends Component {
constructor(props){
super();
this.state = {
completed: props.completed
};
console.log(props);
}
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.completed !== prevProps.completed) {
this.setState({
completed: this.props.completed
});
}
}
render() {
return(
<ul>
{this.state.completed.map((list,index)=>(
<li className="list-group-item list-group-item-success" key={index}> `Task: {list.name} was completed by {list.webmaster} on ticket {list.ticketId}`</li>))
}
</ul>
)
}
}
// Completed.PropTypes={
// test:PropTypes.string.isRequired
export default Completed

Related

How to access function from different components React

Here's the code for Panel
`
import React from "react";
// import {render} from "react-dom";
import AddInventory from "components/AddInventory";
class Panel extends React.Component{
constructor(props) {
super(props);
this.state = {
activeIndex: ''
}
}
componentDidMount() {
this.activePanel();
}
closePanel=()=>{
this.setState({
activeIndex : false
})
}
activePanel = ()=>{
this.setState({
activeIndex : true
})
}
render(){
return(
<div>
{/*<button className={"button is-primary add-btn"} onClick={this.activePanel}>add</button>*/}
<div className={this.state.activeIndex ? 'panel-wrapper active':'panel-wrapper'}>
<div className={"over-layer"}>
<div className={"panel"}>
<div className={"head"}>
<span onClick={this.closePanel} className={"close"}>x</span>
<AddInventory></AddInventory>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default Panel;
Products:
import React from "react";
import ToolBox from "components/ToolBox";
import Product from "components/Product";
import axios from 'components/axios'
import {CSSTransition , TransitionGroup} from 'react-transition-group'
import Panel from "components/Panel";
class Products extends React.Component{
product =[];
source =[];
state ={
product : [{
id:'1',
name:'Air Jordan1',
tags:'45 colours',
image:'images/1.jpg',
price:'21000',
status:'available'
},
{
id:'2',
name:'Nike Pual George PG 3',
tags:'45 colours',
image:'images/2.jpg',
price:'11000',
status:'available'
},
{
id:'3',
name:'Jordan Why Not Zer0.2',
tags:'10 colours',
image:'images/3.jpg',
price:'15000',
status:'unavailable'
},
]
}
componentDidMount() {
// fetch('http://localhost:3003/products').then(response => response.json()).then( data=>{
// console.log(data)
// this.setState({
// product : data
// })
// })
axios.get('/products').then(response => {
this.setState( {
product : response.data,
source : response.data
})
})
}
search = text=>{
//1.get a new array from product
let _product = [...this.state.source]
//2.filter the array
let res = _product.filter((element)=>{
return element.name.toLowerCase().includes(text.toLowerCase())
})
//set state
this.setState({
product : res
})
}
add = ()=>{
let panel = new Panel(this.props)
panel.activePanel()
}
// add =()=>{
// panel.setState({
// activeIndex : true
// })
// }
render() {
return(
<div>
<ToolBox search={this.search}/>
<div className={'products'}>
<div className="columns is-multiline is-desktop">
<TransitionGroup component={null}>
{
this.state.product.map(p=>{
return (
<CSSTransition
timeout={400}
classNames="product-fade"
key={p.id}
>
<div className="column is-3" key={p.id}>
<Product product={p}/>
</div>
</CSSTransition>
)
})
}</TransitionGroup>
{/*<div className="column is-3">*/}
{/* <Product/>*/}
{/*</div>*/}
{/*<div className="column is-3">*/}
{/* <Product/>*/}
{/*</div>*/}
</div>
<button className={"button is-primary add-btn"} onClick={this.add}></button>
</div>
</div>
)
}
}
export default Products;
I was trynna use activePanel() in Products but it gives me : Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign tothis.statedirectly or define astate = {};` class property with the desired state in the Panel component.
I tried initialize a new panel() but it still gives me the same error.
welcome. I don't think this approach is best practice. Generally, components should only ever be updating their own state (see here) and typically you want data to flow from parent component to child component (see here). Additionally, your design is deceptive. When you render a component, you declare it as JSX in some render (or return) statement. But here, Panel is never formally instantiated in JSX.
In Panel, I would suggest watching a prop such as active via shouldComponentUpdate and updating state based on changes to that prop. Then in Products you can instantiate an instance of Panel in JSX and dynamically set the value of that prop.

Array has duplicated records when using checkboxes to populate an array using React

I have trouble with simple task of adding elements selected in checkboxes to an array in component state. It seems like the push method for state.toppings (Editor.js) is invoked twice for each checkbox click, even though console.log shows that updateFormValueCheck method is invoked once per click. Can anyone help?
This is App.js
import React, { Component } from "react";
import { Editor } from "./Editor";
import { Display } from "./Display";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
formData: {}
}
}
submitData = (newData) => {
console.log("newData", newData)
this.setState({ formData: newData });
}
render() {
return <div className="container-fluid">
<div className="row p-2">
<div className="col-6">
<Editor submit={this.submitData} />
</div>
<div className="col-6">
<Display data={this.state.formData} />
</div>
</div>
</div>
}
}
This is Editor.js
import React, { Component } from "react";
export class Editor extends Component {
constructor(props) {
super(props);
this.state = {
toppings: ["Strawberries"]
}
this.toppings = ["Sprinkles", "Fudge Sauce",
"Strawberries", "Maple Syrup"]
}
updateFormValueCheck = (event) => {
event.persist();
this.setState(state => {
if (event.target.checked) {
state.toppings.push(event.target.name);
} else {
let index = state.toppings.indexOf(event.target.name);
state.toppings.splice(index, 1);
}
}, () => this.props.submit(this.state));
}
render() {
return <div className="h5 bg-info text-white p-2">
<div className="form-group">
<label>Ice Cream Toppings</label>
{this.toppings.map(top =>
<div className="form-check" key={top}>
<input className="form-check-input"
type="checkbox" name={top}
value={this.state[top]}
checked={this.state.toppings.indexOf(top) > -1}
onChange={this.updateFormValueCheck} />
<label className="form-check-label">{top}</label>
</div>
)}
</div>
</div>
}
}
This is Display.js
import React, { Component } from "react";
export class Display extends Component {
formatValue = (data) => Array.isArray(data)
? data.join(", ") : data.toString();
render() {
let keys = Object.keys(this.props.data);
if (keys.length === 0) {
return <div className="h5 bg-secondary p-2 text-white">
No Data
</div>
} else {
return <div className="container-fluid bg-secondary p-2">
{keys.map(key =>
<div key={key} className="row h5 text-white">
<div className="col">{key}:</div>
<div className="col">
{this.formatValue(this.props.data[key])}
</div>
</div>
)}
</div>
}
}
}
The output is:
You cannot directly mutate this.state, it can only be done using this.setState. For more info. refer this: Why can't I directly modify a component's state, really?
Therefore, you need to update your Editor component as follows.
componentDidMount is used to display the initial state during the initial rendering. Then componentDidUpdate is used to render the state changes through display component whenever it's updated.
import React, { Component } from "react";
export class Editor extends Component {
constructor(props) {
super(props);
this.state = {
toppings: ["Strawberries"],
};
this.toppings = ["Sprinkles", "Fudge Sauce", "Strawberries", "Maple Syrup"];
}
updateFormValueCheck = (event) => {
event.persist();
let data;
if (event.target.checked) {
data = [...this.state.toppings, event.target.name];
} else {
const index = this.state.toppings.indexOf(event.target.name);
const temp = [...this.state.toppings];
temp.splice(index, 1);
data = temp;
}
this.setState({
toppings: data,
});
};
componentDidMount() {
this.props.submit(this.state.toppings);
}
componentDidUpdate(prevPros, prevState) {
if (prevState.toppings !== this.state.toppings) {
this.props.submit(this.state.toppings);
}
}
render() {
console.log(this.state);
return (
<div className="h5 bg-info text-white p-2">
<div className="form-group">
<label>Ice Cream Toppings</label>
{this.toppings.map((top) => (
<div className="form-check" key={top}>
<input
className="form-check-input"
type="checkbox"
name={top}
value={this.state[top]}
checked={this.state.toppings.indexOf(top) > -1}
onChange={this.updateFormValueCheck}
/>
<label className="form-check-label">{top}</label>
</div>
))}
</div>
</div>
);
}
}
Hope this would be helpful to solve your issue.

the Cardlist component is not getting called upon changing state

I'm new to react js and is having some problem in my code. I have a Cardlist component which is returning my contact list layout when i pass my contactlist into it. I added a searchbox to search for a particular contact. The list updated upon entering some text in search box (I logged it out in console and it is working finely). The render and return methods are also working well upon changing search text ( i.e they are getting called everytime after changing state, I logged them out) but the Cardlist component is not getting called again. It still shows the same old list. The console.log inside this cardlist component is also not logging anything which simply implies it is not called again.
Here is the main component (not whole code shown, only the necessary part) :
constructor(props){
super(props);
this.state = {
friendslist: this.props.data.friendslist,
searchfield:''
}
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value });
}
render(){
var filterfriendslist = this.state.friendslist.filter(friendslistitem => {
return friendslistitem.name.toLowerCase().includes(this.state.searchfield.toLowerCase())
});
{console.log( filterfriendslist )}
return(
<div>
<input id="name" onChange={this.onSearchChange} className="input-reset ba b--black-20 pa2 mv2 db w-100 bg-near-white" type="text" placeholder='Search' />
<Scroll>
<div>
{console.log( filterfriendslist )}
<Cardlist friendlist={ filterfriendslist } loadChattingUser={ this.loadChattingUser } />
</div>
</Scroll>
</div>
);
}
}
export default Contacts;
Here is the Carlist component :
import React from 'react';
import Card from './Card';
class Cardlist extends React.Component {
constructor(props){
super(props);
console.log("Clicked");
}
cardComponent = this.props.friendlist.map((user, i) => {
return <Card key={i} id={this.props.friendlist[i].id} name = {this.props.friendlist[i].name} imageURL={this.props.friendlist[i].imageurl} email={this.props.friendlist[i].email} msgDatabase={ this.props.friendlist[i].msgdata } loadChattingUser={ this.props.loadChattingUser } />
})
render(){
return (
<div>
{this.cardComponent}
</div>
);
}
}
export default Cardlist;
Here is the card component :
import React from 'react';
class Card extends React.Component {
constructor(props){
super(props);
this.state = {
name: this.props.name,
imageURL: this.props.imageURL,
email: this.props.email,
msgDatabase: this.props.msgDatabase
}
}
fillChat = () => {
this.props.loadChattingUser(this.state);
}
render(){
return (
<div className="dt w-100 bb b--black-05 pb2 mt2 pa2 bg-near-white pointer" onClick={ this.fillChat }>
<div className="dtc w2 w3-ns v-mid">
<img alt="Profile" src={this.props.imageURL} className="ba b--black-10 db br-100 w2 w3-ns h2 h3-ns"/>
</div>
<div className="dtc v-mid pl3">
<h1 className="f6 f5-ns fw6 lh-title black mv0">{this.props.name}</h1>
<h2 className="f6 fw4 mt0 mb0 black-60">{this.props.email}</h2>
</div>
<div className="dtc v-mid">
<form className="w-100 tr">
<button className="f6 button-reset bg-white ba b--black-10 dim pointer pv1 black-60" type="submit">+ Follow</button>
</form>
</div>
</div>
);
}
}
export default Card;
I want the Cardlist layout to change according to input in the search but it remain same as the initial list
The issue on your implementation of Cardlist. In your implementation cardComponent field is precomputed field. It supposed to be like this:
class Cardlist extends React.Component {
constructor(props) {
super(props);
console.log("Clicked");
}
render() {
const cardComponent = this.props.friendlist.map((user, i) => {
return (
<Card
key={i}
id={this.props.friendlist[i].id}
name={this.props.friendlist[i].name}
imageURL={this.props.friendlist[i].imageurl}
email={this.props.friendlist[i].email}
msgDatabase={this.props.friendlist[i].msgdata}
loadChattingUser={this.props.loadChattingUser}
/>
);
});
return <div>{cardComponent}</div>;
}
}
Here is codesandbox link: https://codesandbox.io/s/chatter-zdljq

How to reset father component onclick react

I'm doing a menu with a submenu with React. Clicking on the main menu (Example: CRM) creates the submenu (Example Hijo: Argumentarios) but when clicking on the following menu item (Example Padre: Permisos) "Argumentarios" is not deleted and the submenu of "Permisos" is also created.
I need that when pressing in the menu it shows its submenu and eliminates those of the other siblings.
This is a part of my json file
[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"116",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"47",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"51",
"Padre":"Telefonia",
"Hijo":"Audio"
}
]
For confidential reasons I can not show the actual file.
In my next code I change the fetch for a fake json url
import React, { Component } from 'react';
import Icon from './Icon';
class Nav extends Component{
constructor(props){
super(props)
this.state = {
menuSubmenu:[],
}
}
componentWillMount(){
fetch('fake/json_menu.php')
.then(response => response.json())
.then(menuSubmenu =>{
menuSubmenu.forEach(datos => {
let data = {
menu:datos.Padre,
submenu:datos.Hijo,
id:datos.Id,
descripcion:datos.Description,
url:datos.URL
}
this.setState({
menuSubmenu:this.state.menuSubmenu.concat([data])
})
})
})
}
render() {
if (this.state.menuSubmenu.length > 0) {
return(
<nav className="nav">
<div className="menu">
<ul className="list">
{this.state.menuSubmenu.map(datos => <Menu key={datos.id} menu={datos.menu} submenu={datos.submenu} descripcion={datos.descripcion} url={datos.url}/>)}
</ul>
<div className="content-bnt">
<button id="desplegar" className='btn btn--rounded'>
<Icon icon="flecha" className='ico-flecha'/>
</button>
</div>
</div>
</nav>
);
}
return (<p>Cargando usuarios...</p>);
}
}
class Menu extends Component{
constructor(props){
super(props)
this.state = {
abrirSubmenu: false,
}
this.submenuClick = this.submenuClick.bind(this);
}
submenuClick() {
this.setState(state => ({
abrirSubmenu: !state.abrirSubmenu
}));
//alert('Click!')
}
render(){
return (
<>
<li className="list__item">
<button title={this.props.menu} id={"mn-" + this.props.menu} className="desplegable" onClick={this.submenuClick.bind(this)}><Icon icon="auriculares" className='ico-auriculares'/>{this.props.menu}</button>
{
this.state.abrirSubmenu
? (
<div id="sb-crm" className="submenu">
<h3 className="nav--title"><Icon icon="descargar" className='ico-descargar'/>{this.props.submenu}</h3>
<ul className="list">
<li className="list__item">
<a href={this.props.url} title={this.props.descripcion}>{this.props.descripcion}</a>
</li>
</ul>
</div>
)
: (
null
)
}
</li>
</>
)
}
}
export default Nav;
I hope to show only one of the menu items at the same time.
¡Thanks a lot!

React recursively call method on children

I'm making a collapsible list with React. So far it works but now I want to implement a button that expands/collapses everything. Therefore the button need to adjust the state of all elements. I'm not sure what's the best way to tackle this problem though. This is what I have:
import React, {Component} from 'react';
class CollapsibleList extends Component {
constructor(props) {
super(props);
this.state = {
collapsed: true
};
this.subLists = [];
this.papers = [];
if (this.props.subtitles) {
for (let subList of this.props.subtitles) {
this.subLists.push(
<CollapsibleList level={this.props.level + 1} subtitles={subList.subtitles} title={subList.title}/>
);
}
}
this.toggleCollapse = this.toggleCollapse.bind(this);
this.expandAll = this.expandAll.bind(this);
this.collapseAll = this.collapseAll.bind(this);
}
expandAll() {
this.setState({collapsed: false});
this.subLists.forEach(subList => subList.expandAll());
}
collapseAll() {
this.setState({collapsed: true});
this.subLists.forEach(subList => subList.collapseAll());
}
toggleCollapse() {
this.setState(prevState => {
return {collapsed: !prevState.collapsed};
});
}
render() {
return (this.state.collapsed ?
<li className={'collapsibleListItem'}>
<div onClick={this.toggleCollapse}>
{this.props.title}
</div>
<img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
<img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
</li> :
<li className={'collapsibleListItem'}>
<div onClick={this.toggleCollapse}>
{this.props.title}
</div>
<img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
<img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
<ul className={'collapsibleList'}>
{this.subLists}
</ul>
</li>
);
}
}
export default CollapsibleList;
Unfortunately, that doesn't seem to work though.
I can't understand what you are trying to do in your code but you should have 2 different components; one for the list and one for the list item. It should be something like this:
// Parent component
import React from 'react';
import ListItem from './ListItem';
class List extends React.Component {
constructor() {
super();
this.state = {
collapsed: false
}
}
render() {
const data = ['abc', 'def', 'ghi']; // whatever you want to have
return(
<div>
<button onClick={() => this.setState({collapsed: !this.state.collapsed})}>
Collapse
</button>
<ul>
{
this.state.collapsed &&
data.map((val, key) => {
return(
<li>
<ListItem value={val} key={key} />
</li>
)
})
}
</ul>
</div>
)
}
}
And this is the child component
// child component
import React from 'react';
class ListItem extends React.Component {
constructor() {
super();
}
render() {
return(
<div>
{/*// render anything you want*/}
<p>{this.props.value}</p>
</div>
)
}
}
export default ListItem;
This code is just to give you an insight.

Resources