i'm beginner on react(javascript), doing todo app, have a questions related to having a return inside if statement but returning nothing(if user writes nothing and press enter it does not count it), but when i take it away it will affect the behaviour of todo app. My question is what is the meaning of this part of the code if (!value) { return }
if there is no value it will return what ? and why taking away the 'return' it will change the behaviour of todo app ?
here is code:
import React, { useState } from 'react';
import './App.css';
function Todo({ tod, index }) {
return <div className="todo">{tod.text}</div>
}
function TodoForm({ addTodo }) {
const [value, setValue] = useState('');
console.log(value)
const handleSubmit = e => {
e.preventDefault();
if (!value) {
return
}
addTodo(value);
setValue('');
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
className="input"
value={value}
placeholder="Add Todo..."
onChange={e => setValue(e.target.value)}
/>
</form>
)
}
function App() {
const [todos, setTodos] = useState([
{
text: ' Learn by doing',
isCompleted: false
}
]);
const addTodo = text => {
const newTodos = [...todos, { text }];
setTodos(newTodos);
}
return (
<div className="app">
<div className="todo-List">
{todos.map((todo, index) =>
<Todo key={index} index={index} tod={todo} />
)}
<TodoForm addTodo={addTodo} />
</div>
</div>
)
}
export default App;
here i have javascript code where i'm trying to have same kind of statement as the code above ( if(!text){ return } )
here it does not matter if i have 'return' or dont have, it is the same both ways(why in previous example it was different ?)
:
function myFunction(text) {
var text=document.getElementById('text').value
event.preventDefault();
console.log(text);
if(!text){
return
}
else{
document.getElementById('demo').innerHTML = "Text typed: "+text; // if nothing
}}
<form onsubmit="myFunction()">
<input type="text" id="text" >
</form>
<p id="demo"></p>
English is not my mother language so sorry for mistakes.
Copying your code in question here:
const handleSubmit = e => {
e.preventDefault();
if (!value) {
return
}
addTodo(value);
setValue('');
}
This is considered a null check. What you're saying inside of that function is effectively "if this variable is true, don't proceed with the rest of the function and return as undefined". If your value variable equates to a value that is null or undefined in Javascript (attached link goes to a SO answer for the difference between null and undefined) then your ! operator will return true for the if statment again (as I said above) not running the rest of the function and returning undefined.
See this codepen here where if you toggle the variable "shouldRun" from true and false you'll either get the console log after the return, or undefined returned: https://codepen.io/kjscott27/pen/wvMbKJb?editors=1111
EDIT: I felt it was necessary to perhaps add additional sources for the difference between null and undefined. Additionally this resource has answers towards definitions of both terms directly from ECMA script and further reading, here in this other SO answer.
Adding onto kjs0's answer,
In Javascript, returning no value will be undefined. Example:
function myFunc() {
return;
}
myFunc() // the returned value is undefined
Related
the requirement is a bit tricky. dunno how to explain it but I'll try.
so I've 20 forms on my web page. and there's only one submit button that'll bulk-submit all the forms. but filling out all forms isn't required. the user will fill up as many forms as they like. but whichever form they fill up, must be fully filled. means all inputs are required on each form.
so I need to write a logic that'll make all the inputs required in a form if any of the input is filled there. I know I can use the onChange function and write a logic to make all the input required in one form. but I also need to remove the required from the inputs, if all the input field is again cleared. and I think removing the required from the inputs is the main complexity here. Because while adding required i can simply check if any of the inputs have value in it (using onChange on every input). but if I do the same for removing required, I can't be assured if one input field is cleared or all the inputs are cleared from that form. so in simpler words, I need to sync all the inputs on each form.
[NOTE-1: I'm in a React environment, so I've all the facilities of states and stuff]
[NOTE-2: I've made a react component for the form and looped over it 20 times. so you can think that as one form]
[NOTE-3: This is one of my client's projects. so I can't have changes to the requirements]
This is a pretty "business specific" problem, but I would tackle it along these lines. You may need to make adjustments to fit your exact requirements, but the general gist is there.
The key is to treat the "required" flag for each input as "derived" or calculated state. You said "but I also need to remove the required from the inputs" - I don't think that's entirely true, or doesn't fit the react model. You just need to check if other fields are populated in the current form in the current render.
const { useState } = React;
const { render } = ReactDOM;
const forms = [
{
inputs: ["field1", "field2"]
},
{
inputs: ["field3", "field4"]
}
];
function MegaForm() {
const [values, setValues] = useState(() => {
const values = {};
forms.forEach((form) => {
form.inputs.forEach((input) => {
values[input] = "";
});
});
return values;
});
const submit = () => {
console.log(values);
};
const isRequired = (formIndex) => {
return forms[formIndex].inputs.find(
(inputName) => values[inputName] !== ""
);
};
return (
<div>
{forms.map((form, i) => (
<form key={i}>
<h2>Form {i}</h2>
{form.inputs.map((input, j) => (
<div key={j}>
<label>
{input}
<input
value={values[input]}
onChange={(e) =>
setValues({ ...values, [input]: e.target.value })
}
required={isRequired(i)}
/>
{isRequired(i) ? "*" : ""}
</label>
</div>
))}
</form>
))}
<br />
<br />
<button type="button" onClick={submit}>
Submit
</button>
</div>
);
}
render(<MegaForm />, document.getElementById("app"));
CodePen: https://codepen.io/chrisk7777/pen/RwYWWqV?editors=0010
If you have all the forms with the same fields you could go with a solution like this:
export function FormsContainer() {
const [formData, setFormData] = React.useState({});
function onChangeGenerator(i: number) {
return (e) => {
setFormData((data) => ({
...data,
[i]: {
...data[i],
[e.target.name]: e.target.value,
},
}));
};
}
function fieldHasValue(value) {
return value !== null && value !== undefined && value !== '';
}
function formHasValidFields(i) {
return (
formData[i] &&
Object.keys(formData[i]).some((key) => fieldHasValue(formData[i][key]))
);
}
function submit() {
const result = Object.keys(formData).reduce((acc, i) => {
if (formHasValidFields(i)) {
acc.push(formData[i]);
}
return acc;
}, []);
console.log(result);
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
submit();
}}
>
{[0, 1, 2, 3, 4, 5].map((i) => (
<SingleForm
key={i}
onChange={onChangeGenerator(i)}
required={formHasValidFields(i)}
/>
))}
<br />
<br />
<button type="submit">Submit</button>
</form>
);
}
function SingleForm({
required,
onChange,
}: {
required: boolean;
onChange: (e) => void;
}) {
return (
<React.Fragment>
<hr />
<input name="prop1" onChange={onChange} required={required} />
<input name="prop2" onChange={onChange} required={required} />
</React.Fragment>
);
}
StackBlitz: https://stackblitz.com/edit/react-ts-utrgbj
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
i want to return jsx if some condition is true if not undefined should be returned.
below is my code,
const showInfo = (item) {
return (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: (condition === 'value1' || condition === 'value2' ) &&
showInfo(item) //should put this condition into showInfo method
})
}}
/>
);
}
what i am trying to do?
the above code works. but now i want to put the condition inside the showInfo method. so if condition is true return jsx and if condition is false should return undefined.
what i have tried?
I have tried something like below
const showInfo = (item) {
return
{(condition === 'value1' || condition === 'value2' ) ? <div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
: undefined
}
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: showInfo(item) //error here
})
}}
/>
);
}
but the above tried code, gives error "Type 'void' is not assignable to type 'ReactNode'" at actions statement.
could someone help me with this. i am not sure if i have used ternary operator properly. thanks.
EDIT
after trying one of the answers provided,
notify is a method that is returned from usehook
and it evaluates to the component below
const Something: React.FC<SomethingProps> = ({
description,
actions,
...props
}) =>
(
<Header>
<Title>{title}</Title>
</Header>
{(description ||actions) && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
);
here the component is displayed when the condition fails in showInfo component.
in showInfo i am returning undefined if condition fails but still in the Something component the is displayed even though i have {description || actions}
i am not sure what is happening here.what is the condition i have to check for actions to not display in this case
i have tried
{(description ||actions !== 'false') && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
and this works. i am wondering why i should specifically mention
actions !== 'false'
instead of actions only
could someone help me with this. thanks.
If you want to return jsx from function you should wrap them inside some component. In this case you cen use <React.Fragment> or just <>. Another problem which I can see is that you probably forgot about arrow in you arrow function. Also don't know from where variable names condition comes from.
const showInfo = (item) => {
return (
<>
{ condition === "value1" || condition === "value2" ? (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
) : undefined}
</>
);
};
Wouldn't it be better to use the useState or useEffect hooks?
Simplified Code Sample right here
WORDS:
In short: My items state is resetting to [] with each NEW checkbox clicked and I dont understand why. But instead I want to use the spread operator and useState hooks to push an new item into the array so it's an array of objects.
Current behavior in detail: I'm creating an object and setting it in state using all (and I mean ALL) manner of React useState hooks like this: setItems((prevState) => [...prevState, { [evt.target.value]: evt.target.checked }]); As I check one item it's added and items becomes an array of objects (it being added over and over again is not the problem; I'll add a check for that later). BUT Here's the problem: when I click a NEW checkbox the items array is set back to [] and isnt concatenated with the prev items—even though I'm using prevState, spread operator, an arrow func as a wrapper, and all that jazz.
Desired behavior: Every time I check a checkbox, I want to update items [] to push a new object into it, which represents all items that have ever been checked. Before you say anything about duplicating: I'll add the check to see if an item is already in the array, and just update it if so. And before I add all items to cart, I'll strip all objects with checked = false states.
Can you help me understand what react lifecycle fundamentals I'm missing here; why is this happening? And how can I fix it?
CODE:
Where this is happening:
Simplified version of InputComponent
const InputComponent = ({ type, itemId, handleSearchQuery, onSubmit }) => {
const [items, setItems] = useState([]);
const captureInput = (evt) => {
if (evt.target.type === 'checkbox') {
setItems((prevState) => [...prevState, { [evt.target.value]: evt.target.checked }]);
}
};
const renderCheckbox = () => {
return (
<form>
<input type={type} name={itemId} value={itemId} onChange={setItem} />
<input name={itemId} type='submit' value='Add to Cart' />
</form>
);
};
return (
<div className='input-bar'>
{renderCheckbox()}
</div>
);
};
export default InputComponent;
Where this component is used:
import React from 'react';
import InputComponent from './InputComponent';
import './ResultsRenderer.css';
function ResultsRenderer({ data }) {
const renderListings = () => {
let listings = data ? data.Search : null;
return listings
? listings.map((item) => {
return (
<div className='cart-row'>
<InputComponent type='checkbox' className='cart-checkbox' itemId={item.imdbID} />
<div key={item.imdbID} className={item.imdbID}>
<img src={`${item.Poster}`} alt={item.Title} />
<div>
Title<em>{item.Title}</em>
</div>
<div>{item.Year}</div>
<em>{item.imdbID}</em>
</div>
</div>
);
})
: null;
};
return <>{renderListings()}</>;
}
export default ResultsRenderer;
items state is doing its job perfectly fine, you misunderstood the situation.
you're using items state inside InputComponent and for each listings item there is one InputComponent and each one have their own items, I think you meant to use items state inside ResultsRenderer Component to chase all selected items.
here is the changes you need to do:
const InputComponent = ({ type, itemId, setItems }) => {
const captureInput = (evt) => {
if (evt.target.type === "checkbox") {
setItems((prevState) => [
...prevState,
{ [evt.target.value]: evt.target.checked }
]);
}
};
return (
<div className="input-bar">
<form>
<input
type={type}
name={itemId}
value={itemId}
onChange={captureInput}
/>
<input name={itemId} type="submit" value="Add to Cart" />
</form>
</div>
);
};
export default InputComponent;
function ResultsRenderer() {
const [items, setItems] = useState([]);
useEffect(() => {
console.log(items);
}, [items]);
const renderListings = () => {
let listings = [
{ itemId: 1, title: "Hello" },
{ itemId: 2, title: "World" }
];
return listings
? listings.map((item) => {
return (
<div className="cart-row">
<InputComponent
type="checkbox"
className="cart-checkbox"
itemId={item.itemId}
setItems={setItems}
/>
<div key={item.itemId} className={item.itemId}>
<div>
Title<em>{item.Title}</em>
</div>
</div>
</div>
);
})
: null;
};
return <>{renderListings()}</>;
}
and here is the working demo: https://codesandbox.io/s/boring-cookies-t0g4e?file=/src/InputComponent.jsx
So, get this, guys. I retrieve some data from Firebase, and I load it into my state, then map it, but I can't seem to do anything useful with it. It gets weirder, because within the map, console.log(mappedValue) returns the value I expect but <h1>{mappedValue}</h1> doesnt. What gives?
The page in question:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { app } from '../helpers/firebaseHelpers.js';
import '../../stylesheets/home.css';
import '../../stylesheets/forms.css';
import '../../App.css';
export class AuthQuotes extends Component {
constructor(props) {
super(props);
this.state = {
quotes: []
};
}
componentDidMount() {
this.getQuotes();
this.props.changePage("quotes");
}
getQuotes() {
this.firebaseRef = app.database().ref(`quotes`);
var quotes = [];
this.firebaseRef.on('child_added', snapshot => {
var quote = snapshot.val();
quote['key'] = snapshot.key;
quotes.push(quote);
this.setState({
quotes: quotes,
});
});
}
renderQuote(quote) {
return (
<h1>hi</h1>
)
}
render() {
console.log(this.state.quotes[0])
return (
<div>
{this.state.quotes.map(quote => {
<input type="text" placeholder={quote.person.fName} />
console.log(quote.person.fName)
})}
</div>
);
}
}
export default AuthQuotes
Explanation of why it is working when you removed the {}:
There is a valid reason behind everything, there are two ways of using arrow functions:
1- Concise body: When we write like this: a.map(el => el*2)
2- Block body: When we write like this: a.map(el => { return el*2; } )
With first one, in a concise body, only an expression is specified, which becomes the explicit return value, and in block body, you must use an explicit return statement inside body.
you forget to return inside map method
this.state.quotes.map(quote =>
{
console.log(quote.person.fName);
return <input type="text" placeholder={quote.person.fName} />
})
when you remove the brackets then the return will be implicit
this.state.quotes.map(quote => <input type="text" placeholder={quote.person.fName} />)
Turns out, it was a silent syntax error with no actual error message, and I stil really have no idea why it happened. Changing this:
{this.state.quotes.map(quote => {
<input type="text" placeholder={quote.person.fName} />
console.log(quote.person.fName)
})}
To this:
{this.state.quotes.map(quote =>
<input type="text" placeholder={quote.person.fName} />
console.log(quote.person.fName)
)}
(i.e. No curly braces { after the delta => made it work. ES6 WTF? If someone can explain to my WHY, WHY DOES THIS HAPPEN TO GOOD PEOPLE, I would greatly appreciate it.