Refactor code to not have setState Html - reactjs

Here's my current code :
(2 Files & Classes : "FoodStandComponent.jsx")
/* ************************************* */
/* ******** IMPORTS ******** */
/* ************************************* */
import uuid from 'uuid/v4';
import { Card, CardBlock, Button, InputGroup, Input } from 'reactstrap';
import React, { Component } from 'react';
import ProviderInfos from '../ProviderInfos/ProviderInfos';
import InputType from './InputType/InputType';
/* ************************************* */
/* ******** VARIABLES ******** */
/* ************************************* */
const propTypes = {
newInput: React.PropTypes.array,
exportInput: React.PropTypes.func,
};
/* ************************************* */
/* ******** COMPONENT ******** */
/* ************************************* */
class FoodStandComponent extends Component {
constructor(props) {
super(props);
this.state = {
newInput: [
{ name: 'Pretzel', id: uuid() },
{ name: 'Curry', id: uuid() },
{ name: 'Wurst', id: uuid() },
],
InValue: '',
};
this.add = this.add.bind(this);
this.remove = this.remove.bind(this);
}
add = (name) => {
const ninput = this.state.newInput.concat({ name, id: uuid(), value: this.state.InValue });
this.setState({
newInput: ninput,
InValue: '',
});
};
remove = (id, name) => {
const toBeRemoved = this.state.newInput.filter(x => x.name === name).pop();
if (toBeRemoved) {
this.setState({
newInput: this.state.newInput.filter(x => x.name !== name).concat(
this.state.newInput.filter(x => x.name === name && x.id !== toBeRemoved.id),
),
});
}
};
render() {
console.log();
const cubeFifteenOrUnder = this.state.newInput.filter(x => x.name === 'Pretzel')
&& this.state.newInput.filter(x => x.name === 'Pretzel').length <= 13;
const dsoFifteenOrUnder = this.state.newInput.filter(x => x.name === 'Curry')
&& this.state.newInput.filter(x => x.name === 'Curry').length <= 13;
const multiFifteenOrUnder = this.state.newInput.filter(name => name === 'Wurst')
&& this.state.newInput.filter(x => x.name === 'Wurst').length <= 13;
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
<InputGroup>
<Input placeholder="Pretzel" />
<ProviderInfos type="Pretzel" />
{ cubeFifteenOrUnder && (
<div className="plus" onClick={() => this.add('Pretzel')}>
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
{ !cubeFifteenOrUnder && (
<div className="plus-off">
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
</InputGroup>
{this.state.newInput.map((mapStorageVariable) => {
if (mapStorageVariable.name === 'Pretzel') {
return (<InputType
id={mapStorageVariable.id}
placeholder={mapStorageVariable.name}
value={mapStorageVariable.value}
onRemove={() => this.remove(mapStorageVariable.id, mapStorageVariable.name)}
/>);
}
return null;
})}
</fieldset>
<fieldset>
<legend>Curries</legend>
<InputGroup>
<Input placeholder="Curry" />
<ProviderInfos type="Curry" />
{ dsoFifteenOrUnder && (
<div className="plus" onClick={() => this.add('Curry')}>
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
{ !dsoFifteenOrUnder && (
<div className="plus-off">
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
</InputGroup>
{this.state.newInput.map((mapStorageVariable) => {
if (mapStorageVariable.name === 'Curry') {
return (<InputType
id={mapStorageVariable.id}
placeholder={mapStorageVariable.name}
value={mapStorageVariable.value}
onRemove={() => this.remove(mapStorageVariable.id, mapStorageVariable.name)}
/>);
}
return null;
})}
</fieldset>
<fieldset>
<legend>Wursts</legend>
<InputGroup>
<Input placeholder="Wurst" />
<ProviderInfos type="Wurst" />
{ multiFifteenOrUnder && (
<div className="plus" onClick={() => this.add('Wurst')}>
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
{ !multiFifteenOrUnder && (
<div className="plus-off">
<i className="fa fa-plus" aria-hidden="true" />
</div>
) }
</InputGroup>
{this.state.newInput.map((mapStorageVariable) => {
if (mapStorageVariable.name === 'Wurst') {
return (<InputType
id={mapStorageVariable.id}
placeholder={mapStorageVariable.name}
value={mapStorageVariable.value}
onRemove={() => this.remove(mapStorageVariable.id, mapStorageVariable.name)}
/>);
}
return null;
})}
</fieldset>
<Button color="secondary">Options</Button>{' '}
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}
}
SearchExtendedComponent.propTypes = propTypes;
export default SearchExtendedComponent;
(and InputTypeComponent.jsx )
/* ************************************* */
/* ******** IMPORTS ******** */
/* ************************************* */
import ProviderInfos from '../../ProviderInfos/ProviderInfos';
import React, { Component } from 'react';
import { Card, CardBlock, Button, InputGroup, Input } from 'reactstrap';
/* ************************************* */
/* ******** VARIABLES ******** */
/* ************************************* */
/* ************************************* */
/* ******** COMPONENT ******** */
/* ************************************* */
export default class InputTypeComponent extends Component {
constructor(props) {
super(props);
this.state = {
type: '',
};
}
onRemove = () => {
this.props.onRemove(this.props.id);
}
onChange = () => {
this.props.onChange(this.props.id);
}
render() {
const { placeholder, id, value } = this.props;
const { type } = this.state;
this.type = placeholder;
return (
<InputGroup key={id}>
<Input placeholder={placeholder} />{value}
<ProviderInfos type={this.type} />
<div className="minus" onClick={this.onRemove}>
<i className="fa fa-minus" aria-hidden="true" />
</div>
</InputGroup>
);
}
}
I'm trying to get a list with "add" and "remove" buttons refactored into one function.
as you can see above thanks to #Jacky Choo's answer & code I'm almost there but the issue is that I've lost the functionality I previously had of having the line I want deleted removed when I click on it's own remove button.
When I click on this minus sign the line with the text and changed checkboxes stays.
and the very last line dissapears.
UPDATE :
Fixed it!
By changing the remove to this I get my intended result. yes the lines below the deleted one are reset but that is for Redux to handle. A big shoutout to #Jacky Choo who basically figured it out for me!
remove = (id, name) => {
this.setState({
newInput: this.state.newInput.filter(x => x.name === name && x.id !== id),
});
};

Tested working on my side (replaced some customized class to normal input box which is not provided)
Adding jsx into the state doesn't seem right, I've amended the code to store food as an array in the state, each of them is mapped to the component that renders the input field.
Hope it helps
import React, { Component } from 'react';
const FoodInput = ({ foodName, id }) => {
return (
<input placeholder={foodName} key={id} />
);
}
export default class PretzelStandComponent extends Component {
constructor(props) {
super(props);
const uuid = require('uuid/v1');
this.state = {
Foods: [
{name: "Pretzel", id: uuid()},
{name: "Curry", id: uuid()},
{name: "Wurst", id: uuid()}
]
}
}
componentDidMount() {
}
addFood(name) {
const uuid = require('uuid/v1');
this.setState({
Foods: this.state.Foods.concat({ name, id: uuid() })
});
}
removeFood(name) {
var foodToBeRemoved = this.state.Foods.filter(x => x.name === name).pop()
if (foodToBeRemoved){
this.setState({
Foods: this.state.Foods.filter(x => x.name !== name).concat(
this.state.Foods.filter(x => x.name === name && x.id !== foodToBeRemoved.id)
)
});
}
}
render() {
return (
<div>
<fieldset>
<legend>Pretzels</legend>
{this.state.Foods.map(food => {
if (food.name === "Pretzel") {
return (<FoodInput foodName={food.name} key={food.id} {...food} />)
}
else
{
return null
}
})}
<button onClick={() => this.addFood("Pretzel")}>Add a Pretzel</button>
<button onClick={() => this.removeFood("Pretzel")}>Remove a Pretzel</button>
</fieldset>
<fieldset>
<legend>Curry</legend>
{this.state.Foods.map(food => {
if (food.name === "Curry") {
return (<FoodInput foodName={food.name} key={food.id} {...food} />)
}
else
{
return null
}
})}
<button onClick={() => this.addFood("Curry")}>Add a Curry</button>
<button onClick={() => this.removeFood("Curry")}>Remove a Curry</button>
</fieldset>
<fieldset>
<legend>Wurst</legend>
{this.state.Foods.map(food => {
if (food.name === "Wurst") {
return (<FoodInput foodName={food.name} key={food.id} {...food} />)
}
else
{
return null
}
})}
<button onClick={() => this.addFood("Wurst")}>Add a Wurst</button>
<button onClick={() => this.removeFood("Wurst")}>Remove a Wurst</button>
</fieldset>
</div>
);
}
}

You could do smth like this:
export default class PretzelStandComponent extends Component {
constructor(props) {
super(props);
this.state = {
handler: 1
};
...
}
handleHTML = () => {
switch(this.state.handler){
case 1:
return this.returnHTML();
}
}
//Set handlerVariable in your functions instead of setting html
//Return html
returnHTML = () => {
return (<div/>);
}
render(){
return(<div>{this.handleHTML()}</div>);
}

The arguably best and easiest solution is to have an array responsible of storing for each of the ingredients, and then map through each of the arrays in the render.
What's more is that you can use just one function for incrementing or decrementing your arrays because all they do is just create a new uuid, but they return the same JSX more or less.
Because of this similarity you can use just these two functions and the only parameter is just the name of the ingredient to add/remove from.
Here's a working demo. I have replaced some of the components, such as <Input /> and <ProviderInfos /> with a <span> just for the demo.
I also replaced your uuid() with a fake key to get it working.
class PretzelStandComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
inputPretzel: [],
inputCurry: [],
inputWurst: []
};
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
increment = (name) => {
//const uuid = require('uuid/v1');
//uuid();
let uuid = this.state['input' + name].length+1;
let n = this.state['input' + name].slice();
n.push(uuid);
this.setState({['input' + name]: n});
}
decrement = (name) => {
let n = this.state['input' + name];
n.pop();
this.setState({['input' + name]: n});
}
render() {
return (
<div>
<div className="main-table">
<fieldset>
<legend>Pretzels</legend>
{this.state.inputPretzel.map(
key => {
return <span>{key}</span>;
})
}
<button onClick={this.increment.bind(this, "Pretzel")}>Add a Pretzel</button>
<button onClick={this.decrement.bind(this, "Pretzel")}>Remove a Pretzel</button>
</fieldset>
<fieldset>
<legend>Curry</legend>
{this.state.inputCurry.map(
key => {
return <span>{key}</span>;
})
}
<button onClick={this.increment.bind(this, "Curry")}>Add Curry</button>
<button onClick={this.decrement.bind(this, "Curry")}>Remove Curry</button>
</fieldset>
<fieldset>
<legend>Wurst</legend>
{this.state.inputWurst.map(
key => {
return <span>{key}</span>;
})
}
<button onClick={this.increment.bind(this, "Wurst")}>Add Wurst</button>
<button onClick={this.decrement.bind(this, "Wurst")}>Remove Wurst</button>
</fieldset>
<button color="secondary">Options</button>{' '}
<button id="btn">Exécuter</button>
</div>
</div>
);
}
}
ReactDOM.render(<PretzelStandComponent />, document.getElementById("app"));
<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="app"></div>

Related

React parent component needs child function to return data

I think I need a call back function, but do not understand the proper syntax given a parent component calling a child function.
Here is the stripped down parent component followed by the function FilesUpload.
I need the File.Name from child returned and setState({fileName}) in parent component.
Hopefully painfully obvious to someone who knows how to do this.
Thank you in advance for solution.
Rob
#davidsz - any ideas?
...
//Stripped down ParentComponent.jsx
import React, { Component } from 'react'
import FilesUpload from "../Services/FilesUpload";
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
fileName: null
}
this.changefileNameHandler = this.changefileNameHandler.bind(this);
}
changefileNameHandler= (event) => {
this.setState({fileName: event.target.value});
}
componentDidMount(){
}
render() {
return (
<div>
<td>this.state.fileName </td>
<FilesUpload onUpdate={this.changefileNameHandler}/>
</div>
)
}
}
export default ParentComponent
//functional service FilesUpload.js
import React, { useState, useEffect, useRef } from "react";
import UploadService from "../Services/FileUploadService";
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const UploadFiles = () => {
const [selectedFiles, setSelectedFiles] = useState(undefined);
const [progressInfos, setProgressInfos] = useState({ val: [] });
const [message, setMessage] = useState([]);
const [fileInfos, setFileInfos] = useState([]);
const progressInfosRef = useRef(null)
useEffect(() => {
UploadService.getFiles().then((response) => {
setFileInfos(response.data);
});
}, []);
const selectFiles = (event) => {
setSelectedFiles(event.target.files);
setProgressInfos({ val: [] });
};
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
return UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({ val: _progressInfos });
})
.then(() => {
toast.info(file.name + " Uploaded")
setMessage((prevMessage) => ([
...prevMessage,
"Uploaded the file successfully: " + file.name,
]));
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({ val: _progressInfos });
setMessage((prevMessage) => ([
...prevMessage,
"Could not upload the file: " + file.name,
]));
});
};
const uploadFiles = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map(file => ({ percentage: 0, fileName: file.name }));
progressInfosRef.current = {
val: _progressInfos,
}
const uploadPromises = files.map((file, i) => upload(i, file));
Promise.all(uploadPromises)
.then(() => UploadService.getFiles())
.then((files) => {
setFileInfos(files.data);
});
setMessage([]);
};
return (
<div>
{progressInfos && progressInfos.val.length > 0 &&
progressInfos.val.map((progressInfo, index) => (
<div className="mb-2" key={index}>
<span>{progressInfo.fileName}</span>
<div className="progress">
<div
className="progress-bar progress-bar-info"
role="progressbar"
aria-valuenow={progressInfo.percentage}
aria-valuemin="0"
aria-valuemax="100"
style={{ width: progressInfo.percentage + "%" }}
>
{progressInfo.percentage}%
</div>
</div>
</div>
))}
<div className="row my-3">
<div className="col-8">
<label className="btn btn-default p-0">
<input type="file" multiple onChange={selectFiles} />
</label>
</div>
<div className="col-4">
<button
className="btn btn-success btn-sm"
disabled={!selectedFiles}
onClick={uploadFiles}
>
Upload
</button>
</div>
</div>
{message.length > 0 && (
<div className="alert alert-secondary" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
<div className="card">
{/* <div className="card-header">List of Files</div> */}
<ul className="list-group list-group-flush">
{!fileInfos &&
fileInfos.map((file, index) => (
<li className="list-group-item" key={index}>
{/* <a href={file.url}>{file.name}</a> */}
</li>
))}
</ul>
</div>
<ToastContainer position="top-center" autoClose={1000}/>
</div>
);
};
export default UploadFiles;
...
I'm not quite sure I understand your question perfectly, but do you want to pass down the changefileNameHandler function as a prop to your FilesUpload functional component?
In this case you can just add props as a paremeter:
const UploadFiles = (props) => { ...
and call it wherever you need it:
props.onUpdate(event)

Todo App in React- Wanted to add button which when clicks deletes the whole todo list

I have created a ToDo App in React. I want to add a single button which when I clicked on removes the whole todo list and shows the message to the user "You don't have any todo's". I am trying to add functionality but can't seem to find a perfect way.
I have given all the Todos a unique id and I also to grab these id's but don't how to use them to remove all Todos from a single button only. Help me. Thanks in advance
here is my main component App.js
import React, { Component } from 'react';
import PrintTodo from "./printtodo"
import Addtodo from "./addTodo"
class App extends Component {
state = {
todos: [
{id:1, content:"Buy Tomatoes"},
]
}
deleteTodo = (id) => {
const todos = this.state.todos.filter(todo => {
return todo.id !== id
})
this.setState({
todos
})
}
addTodo = (todo) => {
todo.id = Math.random()
// console.log(todo)
let todos = [...this.state.todos, todo]
this.setState({
todos
})
}
button = () => {
// console.log(this.state)
const allTodos = this.state.todos.filter(todo => {
console.log(todo)
})
// const id = 10;
// console.log(allTodos)
// allTodos.forEach(todo => {
// // console.log(todo)
// const arr = new Array(todo)
// arr.pop()
// })
}
render(){
// console.log(this.state)
return (
<div className="App">
<div className="container">
<header className="text-center text-light my-4">
<h1>ToDo - List</h1>
<form>
<input type="text" name="search" placeholder="Search ToDo's" className="form-control m-auto"/>
</form>
</header>
<PrintTodo addTodo={this.state.todos} deleteTodo={this.deleteTodo}/>
<Addtodo addTodo={this.addTodo} allTodos={this.button}/>
</div>
</div>
)
}
}
export default App;
PrintTodo Component
import React from 'react'
const printTodo = ({addTodo, deleteTodo, }) => {
// console.log(addTodo)
const todoList = addTodo.length ? (
addTodo.map(todo => {
return (
<ul className="list-group todos mx-auto text-light" key={todo.id}>
<li className="list-group-item d-flex justify-content-between align-items-center">
<span>{todo.content}</span>
<i className="far fa-trash-alt delete" onClick={()=>{deleteTodo(todo.id)}}></i>
</li>
</ul>
)
})
) : (
<p className="text-center text-light">You don't have any ToDo's</p>
)
return (
<div>
{todoList}
</div>
)
}
export default printTodo
AddTodo Component
import React, { Component } from 'react'
class Addtodo extends Component{
state = {
content: ""
}
handleChange = (e) => {
this.setState({
content: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.addTodo(this.state)
this.setState({
content: ""
})
}
render(){
// console.log(this.props.allTodos)
return(
<div>
<form className="text-center my-4 add text-light" onSubmit={this.handleSubmit}>
<label htmlFor="add">Add a New ToDo</label>
<input onChange={this.handleChange} type="text" name="add" id="add" className="form-control m-auto" value={this.state.content}/>
</form>
<button onClick={() => {this.props.allTodos()}}>Clear Whole List</button>
</div>
)
}
}
export default Addtodo
In your app.js make this your button component.
button = () => {
this.setState({todos: []})
})
Resetting your todos to an empty array will delete all your todos.

How to save key in localStorage to be retrieve later when you go back to your page in ReactJS

I have a page, and I want to save the key to be retrieved later. This key is use to determine the last selected active item in the carousel. What do I need to do using localStorage in ReactJS?
import React, { Fragment, Component } from 'react'
import { truncateString } from '#helpers'
import './styles.css'
class RoutineSidebar extends Component {
handleOnClick = key => {
const { currentSlideKey } = this.props;
const isExerciseDone = this.props.skipExerciseValidation(currentSlideKey);
if(isExerciseDone || key < this.props.currentSlideKey) {
if(this.props.skipExerciseValidation(key === 0 ? 0 : key - 1)) {
this.props.setKey(key);
}
} else {
if(key === this.props.currentSlideKey + 1) {
this.props.openSkipExerModal();
}
}
React.useEffect(() => {
localStorage.setItem('selectedRoutine', key);
}, [key]);
}
checkExerciseStatusSkipped = key => {
const { routineList } = this.props;
return routineList[key].skipped;
};
checkExerciseStatusDone = key => {
const { routineList } = this.props;
return routineList[key].done;
}
checkExercisesSelected = key => {
const { routineList } = this.props;
return routineList[key];
}
render() {
const { exercises, currentSlideKey } = this.props;
const todaysRoutineThumbnails = exercises.map((exercise, key) => {
return (
<div key={key} onClick={() => this.handleOnClick(key)} className={key === currentSlideKey ? 'thumbnail-container selected-exercise' : 'thumbnail-container'}>
<div className="row todays-routine">
<div className="col-sm-6">
{
this.checkExerciseStatusSkipped(key) ? <Fragment><i className="fas fa-times-circle status-indicator-warning" />
<div className="scThumb">
<img className='active-thumbnail img-opaque' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
: this.checkExerciseStatusDone(key) ? <Fragment><i className="fas fa-check-circle status-indicator-done" />
<div className="scThumb">
<img className='active-thumbnail img-opaque' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
: !this.checkExerciseStatusDone(key) && !this.checkExerciseStatusSkipped(key) && <Fragment><div className="routine-exercise-counter">{key + 1}</div><div className="scThumb">
<img className='active-thumbnail' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
}
</div>
<div className="col-sm-6">
<div className="thumbnail-info clearfix">
<p className="thumbnail-title">{truncateString(exercise.exerciseName, 30)}</p>
<p className="thumbnail-description">This is the best exercise for back pain ever made</p>
</div>
</div>
</div>
</div>
)
})
return (
<div className="todays-routine-container">
<h1>{this.props.header}</h1>
{todaysRoutineThumbnails}
</div>
)
}
}
export default RoutineSidebar;
You can't use useEffect hook inside a class-based component.
to use localStorage with React you don't need anything, just use it directly.
I guess if you replaced your useEffect code with this code, it will work fine.
setTimeout(() => {
window.localStorage.setItem("selectedRoutine", key);
}, 0);
Just you need to create one util.js file for this kind of comman usages and paste below code and use these functions by just import.
export const getLocalStorage = (key) => {
const localStorageData = localStorage.getItem(key);
if (localStorageData) {
return JSON.parse(localStorageData);
} else {
return null;
}
}
export const setLocalStorage = (key, value) => {
if (key && value) {
localStorage.setItem(key, JSON.stringify(value));
}
}
Please try this
import React, { Fragment, Component } from 'react'
import { truncateString } from '#helpers'
import './styles.css'
class RoutineSidebar extends Component {
constructor(props){
super(props);
this.setState={
activeCarouselItem: ""
}
}
componentDidMount(){
this.setState({
activeCarouselItem: localStorage.getItem('selectedRoutine')?localStorage.getItem('selectedRoutine'): ""
})
}
handleOnClick = key => {
const { currentSlideKey } = this.props;
const isExerciseDone = this.props.skipExerciseValidation(currentSlideKey);
if(isExerciseDone || key < this.props.currentSlideKey) {
if(this.props.skipExerciseValidation(key === 0 ? 0 : key - 1)) {
this.props.setKey(key);
}
} else {
if(key === this.props.currentSlideKey + 1) {
this.props.openSkipExerModal();
}
}
// React.useEffect(() => {
// localStorage.setItem('selectedRoutine', key);
// }, [key]);
this.setState({activeCarouselItem: key })
}
checkExerciseStatusSkipped = key => {
const { routineList } = this.props;
return routineList[key].skipped;
};
checkExerciseStatusDone = key => {
const { routineList } = this.props;
return routineList[key].done;
}
checkExercisesSelected = key => {
const { routineList } = this.props;
return routineList[key];
}
render() {
const { exercises, currentSlideKey } = this.props;
const todaysRoutineThumbnails = exercises.map((exercise, key) => {
return (
<div key={key} onClick={() => this.handleOnClick(key)} className={key === currentSlideKey ? 'thumbnail-container selected-exercise' : 'thumbnail-container'}>
<div className="row todays-routine">
<div className="col-sm-6">
{
this.checkExerciseStatusSkipped(key) ? <Fragment><i className="fas fa-times-circle status-indicator-warning" />
<div className="scThumb">
<img className='active-thumbnail img-opaque' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
: this.checkExerciseStatusDone(key) ? <Fragment><i className="fas fa-check-circle status-indicator-done" />
<div className="scThumb">
<img className='active-thumbnail img-opaque' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
: !this.checkExerciseStatusDone(key) && !this.checkExerciseStatusSkipped(key) && <Fragment><div className="routine-exercise-counter">{key + 1}</div><div className="scThumb">
<img className='active-thumbnail' alt="todays-routine-thumbnail" src={exercise.thumbnail} />
</div>
</Fragment>
}
</div>
<div className="col-sm-6">
<div className="thumbnail-info clearfix">
<p className="thumbnail-title">{truncateString(exercise.exerciseName, 30)}</p>
<p className="thumbnail-description">This is the best exercise for back pain ever made</p>
</div>
</div>
</div>
</div>
)
})
return (
<div className="todays-routine-container">
<h1>{this.props.header}</h1>
{todaysRoutineThumbnails}
</div>
)
}
}
export default RoutineSidebar

how do i toggle disabled between two buttons in react map

I have a list of candidates with two buttons hire and reject. when i press hire it should be disabled and reject stays enabled. When i press reject it should be disabled and hire must be enabled.
{result && result.map(appliedCandidate => {
if (joblist.id === appliedCandidate.jobid) {
return (
<div className="row pb-3">
<div className=" col-md-4 text-left font-weight-bold">
<p className={this.state.applystatus==="hire" ? "text- info" : "text-danger"}>
{appliedCandidate.firstName}
</p>
</div>
<div className="col-md-8">
<div className="row">
<div className="col-4">
<div className="back-btn">
<input id='hire' type='button' ref='hire' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-success card-btn-width" value='hire' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<input id='reject' type='button' ref='reject' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-danger card-btn-width" value='reject' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<Link to={{ pathname: '/individualchat', state: { name: appliedCandidate.firstName, jobid: appliedCandidate.jobid, id: appliedCandidate.id, Title: appliedCandidate.Title } }}>
<button type="button" className="btn btn-info">chat</button>
</Link>
</div>
</div>
</div>
</div>)
}
})}
hireReject = (event) => {
var dis = event.target.setAttribute('disabled','true')
const phoneno = event.target.getAttribute('data-tag');
const id = event.target.getAttribute('name');
const jobid = event.target.getAttribute('data-id');
const applystatus = event.target.value;
{ applystatus === 'hire' ? toastr.success('Successfully hired') : toastr.error('Successfully rejected') }
{ applystatus === 'hire' ? document.getElementById('reject').disabled = false : document.getElementById('hire').disabled = false }
this.setState({
jobid: jobid, id: id, candidatephoneno: phoneno, applystatus: applystatus
}, () => {
this.props.hireReject(this.state)
})
{return applystatus === 'hire' ? 'hired' : 'rejected'}
}
Consider separating the buttons and hiring/rejecting logic into its own component like the following so you can better handle the toggling.
Index.js
import React from "react";
import ReactDOM from "react-dom";
import Candidate from "./Candidate";
import "./styles.css";
class App extends React.Component {
state = {
text: ""
};
render() {
const candidates = [{ name: "Bob" }, { name: "Sam" }, { name: "Jessie" }];
return candidates.map(candidate => {
return <Candidate candidate={candidate} />;
});
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Candidate.js
import React from "react";
class Candidate extends React.Component {
state = {
hired: null
};
handleHire = () => {
this.setState({
hired: true
});
};
handleReject = () => {
this.setState({
hired: false
});
};
render() {
const hired = this.state.hired;
return (
<div>
<h4>{this.props.candidate.name}</h4>
<button
onClick={this.handleHire}
disabled={hired == null ? false : hired}
>
Hire
</button>
<button
onClick={this.handleReject}
disabled={hired == null ? false : !hired}
>
Reject
</button>
</div>
);
}
}
export default Candidate;
Here is a sandbox for your reference as well: https://codesandbox.io/s/zrlyq0l29m

Updating props in note taking app in React

I'm stuck on my note taking app. Basically the App component passes in data to the NoteEntry component through props. Yet I can't figure out how to edit the previous passed text through props within each NoteEntry instance when I click the "edit" button. The edit button is supposed to bring up text inputs to change the content by updating the text and then pressing the save button. Any tips on how to go about it?
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
title: "",
details: ""
}
this.updateTitle = this.updateTitle.bind(this);
this.updateDetails = this.updateDetails.bind(this);
this.submitHandler = this.submitHandler.bind(this);
this.deleteHandler = this.deleteHandler.bind(this);
}
updateTitle(event) {
this.setState({ title: event.target.value });
}
updateDetails(event) {
this.setState({ details: event.target.value });
}
submitHandler(e) {
e.preventDefault();
if (!this.state.title.length || !this.state.details.length) {
return;
}
const newNote = {
newTitle: this.state.title,
newDetails: this.state.details
}
this.setState(prevState => ({
notes: prevState.notes.concat(newNote),
title: "",
details: ""
}))
}
deleteHandler(id) {
this.setState(prevState => ({
notes: prevState.notes.filter(el => el !== id)
}))
}
render() {
return (
<div className="container">
<h1 className="title">React Notes App</h1>
<NoteForm
titleValue={this.state.title}
detailsValue={this.state.details}
titleHandle={this.updateTitle}
detailsHandle={this.updateDetails}
onSubmit={this.submitHandler}
/>
<div className="entry-section">
{this.state.notes.map((note, i) => (
<NoteEntry
key={i}
title={note.newTitle}
details={note.newDetails}
deleteNote={this.deleteHandler.bind(this, note)}
/>
))}
</div>
</div>
);
}
}
const NoteForm = (props) => {
return (
<div>
<form className="form-section">
<input
className="title-input"
type="type"
placeholder="Title"
value={props.titleValue}
onChange={props.titleHandle}
/>
<br />
<textarea
className="details-input"
cols="20"
rows="3"
placeholder="Details"
value={props.detailsValue}
onChange={props.detailsHandle}
/>
<br />
<button
className="input-button"
onClick={props.onSubmit}
>Add Note</button>
</form>
</div>
)
}
class NoteEntry extends Component {
constructor(props) {
super(props);
this.state = {
display: false,
editTitle: this.props.title,
editDetails: this.props.details,
editing: false
}
this.displayToggle = this.displayToggle.bind(this);
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
displayToggle() {
this.setState(prevState => ({
display: !prevState.display
}))
}
edit() {
this.setState({
editing: true
})
}
save() {
let titleVal = this.refs.updateTitle.value;
let detailsVal = this.refs.updateDetails.value;
this.setState({
editTitle: titleVal,
editDetails: detailsVal,
editing: false
})
}
render() {
return (
<div className="entry">
<div className="entry-header" onClick={this.state.editing ? null : this.displayToggle}>
{this.state.editing ? (
<input ref="updateTitle" className="edit-title" type="text" />
) : (
<h2 className="entry-title">{this.props.title}</h2>
)}
<p className="timestamp">{this.displayTime}</p>
</div>
<hr />
<div className={"entry-content " + (!this.state.display ? "hide-details" : null)}>
{this.state.editing ? (
<textarea ref="updateDetails" className="edit-details" cols="10" rows="2"></textarea>
) : (
<p className="details">{this.props.details}</p>
)}
<div className="entry-buttons">
{this.state.editing ? (
<button className="save" onClick={this.save}>Save</button>
) : (
<button className="edit" onClick={this.edit}>Edit</button>
)
}
<button className="delete" onClick={this.props.deleteNote}>Delete</button>
</div>
</div>
</div>
)
}
}
You can do by pass data from child to parent component as mention it in comment.
In you case NoteEntry add onEditNote props. This props use for function by parent (App component) and use by onClick edit button.
<NoteEntry
...
onEditNote={this.handleClickEdit}
/>
then in class NoteEntry
<button className="edit" onClick={() => this.props.handleClickEdit(this.props.title, this.props.detail)}>Edit</button>
So, handleClickEdit handle by App component and set it to your state
handleClickEdit = (_title, _detail) => {
this.setState({title: _title, details: _detail});
}
Now, your NoteForm component able to edit.

Resources