Display a message in semantic ui only after a button is clicked - javascript

In Semantic UI, a message can be visible
<Message visible>You can always see me</Message>
or hidden.
<Message hidden>You can't see me</Message>
I have a button that when clicked gets an API response, that then fills a Message with text. I only want to see the Message after clicking the button, because until then it is just an empty box at the bottom of the screen. How can I implement this in my function?
import React, { useState } from 'react';
import { Button, Header, Message} from 'semantic-ui-react'
export default function Writer() {
const [titleText, setTitleText] = useState('');
const getResponse = () => {
//Does Some Stuff
}).then((response) => {
setTitleText(response.data.choices[0].text)
})
return (
<div>
<Button onClick={getResponse}></Button>
<Message>
<Header
icon textAlign='center'
content={titleText}
/>
</Message>
</div>
)
}
I tried to set a variable
var visibility = 'hidden'
and set visibility = 'visible' inside the function, then writing <Message {visibility}? but it did not work. Any recommendations please?

In Semantic-UI-React the visible & hidden are boolean flags. So you need to pass in a Boolean value. & because you need it to be done based on titleText you can simply use the length property.
import React, { useState } from 'react';
import { Button, Header, Message} from 'semantic-ui-react'
export default function Writer() {
const [titleText, setTitleText] = useState('');
const getResponse = () => {
//Does Some Stuff
}).then((response) => {
setTitleText(response.data.choices[0].text)
})
return (
<div>
<Button onClick={getResponse}></Button>
<Message visible={titleText.length > 0} hidden={titleText.length === 0}>
<Header
icon textAlign='center'
content={titleText}
/>
</Message>
</div>
)
}

Related

Implement Edit feature in a Note Application using react

I am trying to create a notes application wherein each note object contains a title and content. The user can add, deleted and update a note.
What I was able to achieve so far:
I am able to create a new note , push it into an array and also delete a note from the array . I am finding it a bit hard to edit an existing note.
This is how I want to implement the edit feature:
When the user clicks on the note, the data has to automatically fill into the input box, and the user can modify the data which is then saved into an object and pushed inside an array and then displayed onto the respective note.
When the user clicks on the Edit button, the note id is sent to the App component, the note is searched within the notes array and an object returned to the Create Area component. This object is then displayed on the input field. I'm using UseEffect() hook to display the object data on the input box, but I'm not able to edit the contents on the input box. Here's my code below:
App.jsx:
If the user clicked the edit button, it sets the IsDone state to true in the Edit function. The edit function gets an object from the Notes component
import Header from "./Header";
import CreateArea from "./CreateArea";
import Note from "./Note";
import Footer from "./Footer";
import { useState } from "react";
function App() {
const [noteArray, setArray] = useState([]);
const [isDone,setDone] = useState(false);
const [editNote,setEditNote] = useState({
title:"",
content:""});
function AddOnClick(note) {
setArray((prevNote) => {
return [...prevNote, note];
});
}
function DeleteOnClick(id) {
setArray((prevNote) => {
return prevNote.filter((note, index) => {
return index !== id;
});
});
}
function EditNote(obj)
{
setDone(true);
setEditNote(prevState=>{
return { ...prevState,...obj}});
}
return (
<div>
<Header />
<CreateArea AddOnClick={AddOnClick} noteEdit = {editNote} editFunction = {EditNote}btnClicked = {isDone}/>
{noteArray.map((note, index) => (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={DeleteOnClick}
EditNote = {EditNote}
/>
))}
<Footer />
</div>
);
}
export default App;
Notes.jsx: The id of the note is also included in the object that's passed to App component through the EditNote() function
function Note(props) {
const obj = {title : props.title,
content: props.content,
id:props.id}
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button
onClick={() => {
props.deleteNote(props.id);
}}
>
DELETE
</button>
<button onClick={()=>{props.EditNote(obj)}}>EDIT</button>
</div>
);
}
export default Note;
CreateArea: If the buttonClicked value is true, I'm calling the handleEdit() that takes the object sent from the EditNote() in App.jsx to saves it to to note object using useState() which automatically updates the input and text area field using event.target.value with the help of useEffect().
import { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({
title: "",
content: ""
});
function handleChange(event) {
console.log(event.target);
const { name, value } = event.target;
setNote((prevNote) => {
return { ...prevNote, [name]: value };
});
}
function addNote(event) {
setNote({ title: "", content: "" });
props.AddOnClick(note,note.id);
event.preventDefault();
}
function handleEdit()
{
setNote(prevValue=>{
return {...prevValue,...props.noteEdit}
})
}
useEffect (()=>{
if(props.btnClicked){handleEdit();
}
});
return (
<div>
<form>
<input name="title" id="title" value={note.title}onChange={handleChange}placeholder="Title"/>
<textarea name="content" id="content" value={note.content}onChange={handleChange} placeholder="Take a note..." rows="3"/>
<button onClick={addNote}>Add</button>
</form>
</div>
);
}
export default CreateArea;
The code runs well but now I can't add any more text on the input box,it just blocks me from doing it.I tried calling HandleChange() inside UseEffect(), that throws an error saying: Cannot read properties of target:undefined at HandleChange() I really need help how to implement edit.
I tried directly populating the input box and the text area field using document.getElementById.value = myValue even that does not seem to work

Show a loading message with Semantic-UI until an API gathers a response

I have an app that uses an API to get responses, these responses are then rendered in a message. The message only shows up once the last response is rendered. I want to have a loading icon appear as soon as the button that gets the responses is pressed so that users don't spam the button.
Here is my current code:
import React, { useState } from 'react';
import { Button, Header, Message, Icon} from 'semantic-ui-react'
export default function Writer() {
const [titleText, setTitleText] = useState('');
const getResponse = () => {
//Does Some Stuff
}).then((response) => {
setTitleText(response.data.choices[0].text)
})
return (
<div>
<Button onClick={getResponse}></Button>
<Message visible={titleText.length > 0} hidden={titleText.length === 0}>
<Header
icon textAlign='center'
content={titleText}
/>
</Message>
</div>
)
}
I tried to add another message, that should appear when the button is pressed and disappear when the API response is gotten.
<Message visible={loading} hidden={titleText.length > 0}>
<Icon name='circle notched' loading />
</Message>
<Message visible={titleText.length > 0} hidden={titleText.length === 0}>
<Header
icon textAlign='center'
content={titleText}
/>
</Message>
I set loading to false and then true when the button is pressed.
const [loading, setLoading] = useState(false);
const getAPIResponse = () => {
setLoading(true)
This doesn't work, and I think it's because no matter if visible is true or false the icon seems to always be visible. Is there a solution to this?

React - hide content with button

i'm new here. I got a problem with a task. I want a button that if you press it hides some content, a string in this case. If the content is hidden the button must change the text inside it,
and it must say "show" and instead of hiding it shows the content
previously hidden. If the content is already displayed, the button text will be "hide".
I don't understand how to use if statement
...
import React { useState } from "react";
function App() {
const [hideText, setHideText] = useState(false);
const onClick = () => setHideText(false);
return (
<div>
<button onClick={onClick}>Click me</button>
{hideText ? <Text /> : null}
</div>
);
}
const Text = () => <div>I will disappear, true Magic</div>;
export default App;
...
I don't know if I correctly understood your needs.
I changed the variable name to be more meaningful :)
Now the button shows Hide when the text is visible and Show when it's hidden. Clicking the button changes the state.
import React { useState } from "react";
function App() {
const [isTextHidden, setTextHidden] = useState(true);
const onClick = () => setTextHidden(!isTextHidden);
return (
<div>
<button onClick={onClick}>{isTextHidden ? 'Show' : 'Hide'}</button>
{!textHidden ? <Text /> : null}
</div>
);
}
const Text = () => <div>I will disappear, true Magic</div>;
export default App;
import React { useState } from "react";
function App() {
const [isVisible, setVisible] = useState(true);
const onClick = () => setVisible(!isVisible);
return (
<div>
<button onClick={onClick}>{isVisible? 'Hide' : 'Show'}</button>
{isVisible? <Text /> : null}
</div>
);
}
const Text = () => <div>I will disappear, true Magic</div>;
export default App;

How do I add the ability to edit text within a react component?

So here's the user function I'm trying to create:
1.) User double clicks on text
2.) Text turns into input field where user can edit text
3.) User hits enter, and upon submission, text is updated to be edited text.
Basically, it's just an edit function where the user can change certain blocks of text.
So here's my problem - I can turn the text into an input field upon a double click, but how do I get the edited text submitted and rendered?
My parent component, App.js, stores the function to update the App state (updateHandler). The updated information needs to be passed from the Tasks.jsx component, which is where the text input is being handled. I should also point out that some props are being sent to Tasks via TaskList. Code as follows:
App.js
import React, {useState} from 'react';
import Header from './Header'
import Card from './Card'
import cardData from './cardData'
import Dates from './Dates'
import Tasks from './Tasks'
import Footer from './Footer'
import TaskList from './TaskList'
const jobItems= [
{
id:8,
chore: 'wash dishes'
},
{
id:9,
chore: 'do laundry'
},
{
id:10,
chore: 'clean bathroom'
}
]
function App() {
const [listOfTasks, setTasks] = useState(jobItems)
const updateHandler = (task) => {
setTasks(listOfTasks.map(item => {
if(item.id === task.id) {
return {
...item,
chore: task.chore
}
} else {
return task
}
}))
}
const cardComponents = cardData.map(card => {
return <Card key = {card.id} name = {card.name}/>
})
return (
<div>
<Header/>
<Dates/>
<div className = 'card-container'>
{cardComponents}
</div>
<TaskList jobItems = {listOfTasks} setTasks = {setTasks} updateHandler = {updateHandler}/>
<div>
<Footer/>
</div>
</div>
)
}
export default App;
Tasks.jsx
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
TaskList.jsx
import React from 'react'
import Tasks from './Tasks'
function TaskList (props) {
const settingTasks = props.setTasks //might need 'this'
return (
<div>
{
props.jobItems.map(item => {
return <Tasks key = {item.id} item = {item} setTasks = {settingTasks} jobItems ={props.jobItems} updateHandler = {props.updateHandler}/>
})
}
</div>
)
}
export default TaskList
You forgot onChange handler on input element to set item's chore value.
Tasks.jsx must be like below
import React, {useState} from 'react'
function Tasks (props) {
const [isEditing, setIsEditing] = useState(false)
const handleInputChange = (e)=>{
// console.log( e.target.value );
// your awesome stuffs goes here
}
return(
<div className = 'tasks-container'>
{
isEditing ?
<form>
<input type = 'text' onChange={handleInputChange} defaultValue = {props.item.chore}/>
</form>
: <h1 onDoubleClick ={()=> setIsEditing(true)}>{props.item.chore}</h1>
}
</div>
)
}
export default Tasks
So, first of all, I would encourage you not to switch between input fields and divs but rather to use a contenteditable div. Then you just use the onInput attribute to call a setState function, like this:
function Tasks ({item}) {
return(
<div className = 'tasks-container'>
<div contenteditable="true" onInput={e => editTask(item.id, e.currentTarget.textContent)} >
{item.chore}
</div>
</div>
)
}
Then, in the parent component, you can define editTask to be a function that find an item by its id and replaces it with the new content (in a copy of the original tasks array, not the original array itself.
Additionally, you should avoid renaming the variable between components. (listOfTasks -> jobItems). This adds needless overhead, and you'll inevitably get confused at some point which variable is connected to which. Instead say, <MyComponent jobItems={jobItems} > or if you want to allow for greater abstraction <MyComponent items={jobItems} > and then you can reuse the component for listable items other than jobs.
See sandbox for working example:
https://codesandbox.io/s/practical-lewin-sxoys?file=/src/App.js
Your Task component needs a keyPress handler to set isEditing to false when enter is pressed:
const handleKeyPress = (e) => {
if (e.key === "Enter") {
setIsEditing(false);
}
};
Your updateHandler should also be passed to the input's onChange attribute, and instead of defaultValue, use value. It also needs to be reconfigured to take in the onChange event, and you can map tasks with an index to find them in state:
const updateHandler = (e, index) => {
const value = e.target.value;
setTasks(state => [
...state.slice(0, index),
{ ...state[index], chore: value },
...state.slice(index + 1)
]);
};
Finally, TaskList seems like an unnecessary middleman since all the functionality is between App and Task; you can just render the tasks directly into a div with a className of your choosing.
react-edit-text is a package I created which does exactly what you described.
It provides a lightweight editable text component in React.
A live demo is also available.

onBlur / onClick conflict with CodeMirror2 in React

I have created multiple CodeMirror cells. OnBlur works fine, however if I click run button on the other cell, instead of firing run, it actually triggers onBlur and then I need to click mouse again to trigger run. Ideally both of these events should be fired when run button is clicked.
I have seen that the issue is with the order of precedence for these two events and one of the proposed solutions was to add ref attribute to code mirror like this ref = {cell => this.cell = cell} and then in the other handler which is related to run button do this.cell.focus() or in some similar way.
Unfortunately I am not even able to access ref attribute for CodeMirror2 so I can't test it. I will paste both of these components and any suggestion is appreciated.
To summarize: The issue is that onBlur shadows onClick, so run button needs to be clicked twice. I want to be able to click run button and that both onBlur and handleRunClick fire.
import React, { Component } from "react";
import { Controlled as CodeMirror } from "react-codemirror2";
import CellResults from "./CellResults";
import CellToolbar from "../Shared/CellToolbar";
import AddCellButton from "../Shared/AddCellButton";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/darcula.css";
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/ruby/ruby.js";
import "codemirror/mode/python/python.js";
class CodeCell extends Component {
state = {
code: this.props.cell.code
};
handleChange = value => {
this.setState({ code: value });
};
handleBlur = () => {
this.props.onUpdateCodeState(this.state.code, this.props.cellIndex);
if (this.props.language === "Markdown") {
this.props.toggleRender(this.props.cellIndex);
}
};
render() {
const cell = this.props.cell;
const cellOptions = {
mode: cell.type.toLowerCase(),
theme: "darcula",
lineNumbers: true,
showCursorWhenSelecting: true
};
return (
<div>
<div className="add-cell-container">
<AddCellButton
className="add-cell-btn"
onClick={this.props.onAddClick}
cellIndex={this.props.cellIndex}
/>
</div>
<CellToolbar
cellIndex={this.props.cellIndex}
onDeleteClick={this.props.onDeleteClick}
language={cell.type}
onLanguageChange={this.props.onLanguageChange}
rendered={cell.rendered}
onRunClick={this.props.onRunClick}
/>
<CodeMirror
value={this.state.code}
options={cellOptions}
onBeforeChange={(editor, data, value) => {
this.handleChange(value);
}}
onBlur={this.handleBlur}
/>
{cell.type !== "Markdown" ? (
<CellResults language={cell.type} results={cell.results} />
) : null}
</div>
);
}
}
export default CodeCell;
import React from "react";
import Button from "react-bootstrap/Button";
class RunCellButton extends React.Component {
handleRunClick = () => {
this.props.onClick(this.props.cellIndex);
};
render () {
return (
<Button
className="run-button"
onClick={this.handleRunClick}
variant="secondary"
size="sm"
>
<span>►</span>
</Button>
);
}
};
export default RunCellButton;
Below is the similar problem with Adding Cell. When I trigger click event it fires, the state of show is changed to true but if I log it on line 36 it is still false. I am triggering click event from a different component, again due to this obBlue shadowing everything else so every button needs to be clicked twice.
import React, { Component } from "react";
import SplitButton from "react-bootstrap/SplitButton";
import * as constants from "../../Constants/constants";
import DropdownButton from "react-bootstrap/DropdownButton";
import Dropdown from "react-bootstrap/Dropdown";
class AddCellButton extends Component {
state = {
show: false
}
handleSelectCellType = language => {
this.props.onClick(this.props.cellIndex, language);
};
handleToggle = () => {
this.setState((prevState) => {
return {
show: !prevState["show"]
}
})
}
render() {
const dropDownItems = constants.LANGUAGES.map(language => {
return (
<Dropdown.Item
as="button"
value={language}
key={language}
eventKey={language}
>
{language}
</Dropdown.Item>
);
});
console.log("state of show " + this.state.show)
return (
<DropdownButton
show={this.state.show}
onToggle={this.handleToggle}
className="add-cell-btn"
variant="secondary"
id="dropdown-basic-button"
title={<span>+</span>}
size="sm"
onSelect={this.handleSelectCellType}
>
{dropDownItems}
</DropdownButton>
);
}
}
export default AddCellButton;

Categories

Resources