Post content and reply comment using react - reactjs

I am new in react and for educational purposes, I am trying to make social media app that contains a discussion section. I made a recursive textbox for that. Now, I need a line number for new content as well as for the replied comment. Here submit button is used to post new content and the reply button is used to comment on existing content. How can I do this?
Here is my code
import React, { useState } from "react";
import ContentBoxWithVotes from "./content_box_with_votes";
import styled from "#emotion/styled";
// creating a bundler component
const Bundle = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
`;
const ContentBoxRepeatWtihVotesFunctional = () => {
const [buttonName, setButtonName] = useState("stop");
const [isExit, setIsExit] = useState(false);
const [count, setCount] = useState([1]);
const [lineNumber, setLineNumber] = useState(1);
const toogelButton = (text) => {
if (buttonName === "stop") {
setButtonName("Restart");
setIsExit(true);
} else {
setButtonName("stop");
setIsExit(false);
}
};
function handleReply(lineNumber){
setLineNumber(lineNumber+1);
};
const handleChange = (index, value) => {
// setValue({
// values:{
// ...values,
// [index]: value
// }}
// );
};
const handleSubmit = (e) => {
e.preventDefault();
if (!isExit) {
const lastCount = count[count.length - 1] + 1;
setCount([...count, lastCount]);
}
};
return (
<div>
<form onSubmit={handleSubmit}>
{count.map((key) => (
<Bundle>
<label>{lineNumber[key] } </label>
<ContentBox
value={key}
onChange={(event) => handleChange(event.target.value)}
/>
<input type="submit" value="Submit" />
<input type="button" value="Reply" onClick= {handleReply}/>
</Bundle>
))}
</form>
<button onClick={toogelButton}>{buttonName}</button>
</div>
);
};
export default ContentBoxRepeatWtihVotesFunctional;
```
**code inside ContentBox is**
```
import React from 'react';
const ContentBox = () => {
return(
<input type="text" placeholder='Add your content line here ... '/>
);
};
export default ContentBox;

Related

How to handle multiple select options submittion in react js?

I want to submit a form into mongoDB using nodejs API & reactJs. With the exception of the multiple select option, everything is operating as it should be.
Being new to react, I have no idea how to handle the multi select option's onChange method.
Here is what I've tried:
import React, { useState, useRef } from "react";
import { useForm } from "react-hook-form";
import { v4 as uuidv4 } from 'uuid';
import axios from "axios";
import Select from 'react-select';
export default function EventForm(props) {
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm();
const form = useRef();
const [loading, setLoading] = useState(false);
const [info, setInfo] = useState("");
const [analysis, setAnalysis] = useState("Undefined");
const [relatedEvent, setRelatedEvent] = useState([]);
const handleInfoChange = (e) => {
setInfo(e.target.value)
}
const handleAnalysisChange = (e) => {
setAnalysis(e.target.value)
}
const handleRelatedEvents = (e) => {
setRelatedEvent(e.target.value)
}
const relatedEventsData = props.data.map(opt => ({ label: opt.info, value: opt._id }));
const onSubmit = async () => {
setLoading(true);
const MySwal = withReactContent(Swal);
const eventData = {
UUID: uuidv4(),
info: info,
analysis: analysis,
relatedEvent: relatedEvent,
}
axios
.post(`${process.env.REACT_APP_PROXY}/api/events`, eventData)
.then((res) => {
console.log(res);
setLoading(false);
MySwal.fire(
"Success!",
"A new event has been saved successfully",
"success"
);
})
.catch((error) => {
console.log(error);
});
};
return (
<div className="panel-body">
<Form
ref={form}
onSubmit={handleSubmit(onSubmit)}
className="form-horizontal"
>
<div className="row">
<div className="col-lg-6">
<div className="mb-3">
<Form.Label>Info</Form.Label>
<Form.Control
type="text"
placeholder="Enter info..."
{...register("info", { required: true })}
value={info}
onChange={handleInfoChange}
/>
{errors.info && (
<ul className="parsley-errors-list filled" id="parsley-id-7" aria-hidden="false">
<li className="parsley-required">This value is required.</li>
</ul>
)}
</div>
</div>
<div className="col-lg-6">
<div className="mb-3">
<Form.Label>Related events</Form.Label>
<Select
options={relatedEventsData}
value={relatedEvent}
isMulti
onChange={handleRelatedEvents}
/>
</div>
</div>
<div className="col-lg-12">
<Button variant="primary" type="submit">
{loading ? "Saving..." : "Save"}
</Button>
</div>
</div>
</Form>
</div>
);
}
Could you please guide me how to make it work!
Thank you
you can make use of Select onChange event handler which passes the selected options as an array as argument ..
from that you can map over it to get the values as required
something as below:
const handleChange = (opts) => {
const selectedValues = opts.map((opt) => opt.value);
setSelectedValues(selectedValues);
};
Please check the working sample for better clarity 😉 -

Why is react-google-recaptcha-v3 spamming network requests in my ReactJS component?

Using the package react-google-recaptcha-v3, I am able to get a score for the v3 captcha from google when I submit my form, great! However... If I hope the network tab of chrome I see a neverending loop of requests going out to recaptcha (way before I ever submit the form). Many every second:
https://www.google.com/recaptcha/api2/reload?k=xxxx (where xxxx is my recaptcha site key)
Is it something from my reactJS component? I can't imagine this is supposed to happen right.
My code is below, I have stripped out the irrelevant content and made the form small for readability.
import React, { useState, useCallback } from 'react'
import config from 'config'
import {
GoogleReCaptchaProvider,
GoogleReCaptcha
} from "react-google-recaptcha-v3"
function ContactForm(props) {
/*form data*/
const [name, setName] = useState('')
/*validation state*/
const [noNameError, setNoNameError] = useState(false)
/*recaptcha state*/
const [token, setToken] = useState();
const [refreshReCaptcha, setRefreshReCaptcha] = useState(false);
const key = config.RECAPTCHA_V3_SITEKEY
const onVerify = useCallback((token) => {
setToken(token);
});
const getIP = async()=>{
const response = await fetch('https://geolocation-db.com/json/');
const data = await response.json();
return(data.IPv4)
}
const handleSubmit = (event) => {
event.preventDefault()
if(!doValidationStuff()){
setNoNameError(true)
}
setNoNameError(false)
const userIpGetter = getIP()
userIpGetter.then(function(ipResult){
myService.doStuff(
name,
token,
ipResult
)
.then(()=>{
doOtherStuff()
setRefreshReCaptcha(r => !r)
})
})
}
const setFormName = (event)=>{
setName(event.target.value)
}
return (
<GoogleReCaptchaProvider reCaptchaKey={key}>
<form id="contactForm" onSubmit={handleSubmit} className="needs-validation">
<GoogleReCaptcha
action='ContactForm'
onVerify={onVerify}
refreshReCaptcha={refreshReCaptcha}
/>
<div className="mb-3">
<label className="form-label">Name</label>
<input className="form-control" type="text" placeholder="Name" value={name}
onChange={setFormName}/>
<span style={{ color: "red", display: noNameError ? 'block' : 'none' }}>Please enter your name.</span>
</div>
<div className="d-grid">
<button className="btn btn-primary btn-lg" type="submit">Submit</button>
</div>
</form>
</GoogleReCaptchaProvider>
)
}
export { ContactForm };
I ended up having to use the hook from this lib in case anyone else runs into this. Unsure if the refresh is needed in this case, so far I am not doing manual refreshes of the token, leaving that up to the recaptcha magic. Here is the code I ended up with that works, I have stripped out the other parts of the component for readability, but it should still build/run for you:
Way out at the top level of the app:
<GoogleReCaptchaProvider reCaptchaKey={config.RECAPTCHA_V3_SITEKEY}>
<App />
</GoogleReCaptchaProvider>
Then way drilled down into a specific component:
import React, { useState, useEffect, useCallback } from 'react'
import { useGoogleReCaptcha } from 'react-google-recaptcha-v3'
function ContactForm(props) {
const [isSaving, setIsSaving] = useState(false)
/*form data*/
const [name, setName] = useState('')
/*validation state*/
const [noNameError, setNoNameError] = useState(false)
/*recaptcha state*/
const [recToken, setRecToken] = useState()
/*START: recaptcha code*/
const { executeRecaptcha } = useGoogleReCaptcha()
const handleReCaptchaVerify = useCallback(async () => {
if (!executeRecaptcha) {
console.log('Execute recaptcha not yet available');
return;
}
const recTokenResult = await executeRecaptcha('contactForm')
setRecToken(recTokenResult)
}, [executeRecaptcha]);
useEffect(() => {
handleReCaptchaVerify();
}, [handleReCaptchaVerify]);
/*END: recaptcha code*/
const getIP = async()=>{
const response = await fetch('https://geolocation-db.com/json/');
const data = await response.json();
return(data.IPv4)
}
const handleSubmit = (event) => {
event.preventDefault()
/*validation start*/
if(!name || name.length < 3){
setNoNameError(true)
return
}
else{
setNoNameError(false)
}
/*validation end*/
const userIpGetter = getIP()
handleReCaptchaVerify().then(function(){
userIpGetter.then(function(ipResult){
blahService.sendContactForm(
name,
recToken,
ipResult
)
.then(()=>{
blahService.success('Thank you!')
})
})
})
}
const setFormName = (event)=>{
setName(event.target.value)
}
return (
<form id="contactForm" onSubmit={handleSubmit} className="needs-validation">
<div className="mb-3">
<label className="form-label">Name</label>
<input className="form-control" type="text" placeholder="Name" value={name}
onChange={setFormName}/>
<span style={{ color: "red", display: noNameError ? 'block' : 'none' }}>Please enter your name.</span>
</div>
<div className="d-grid">
<button className="btn btn-primary btn-lg" type="submit">Submit</button>
</div>
</form>
)
}
export { ContactForm };

How do I edit form data in a React function component?

I'm trying to set a form field value with useState.
The settings.values.apiKey variable has a value, but the textarea element is empty. What's wrong with my useState?
I tried to change value={apiKey} to value={settings.values.apiKey} and then the value is displayed, but then I can't change the value of the field. When I try to enter something, it always shows the original value.
App.js
const App = () => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
useEffect(() => {
const getSettings = async () => {
const settingsFromServer = await fetchSettings()
setSettings(settingsFromServer)
}
getSettings()
}, [])
const fetchSettings = async () => {
const res = await fetch('http://127.0.0.1/react-server/get.php')
return await res.json()
}
const saveSettings = async (settings) => {
}
return (
<div className="container">
<Header />
<Settings
settings={settings}
saveSettings={saveSettings}
/>
<Footer />
</div>
);
}
export default App;
Settings.js:
import { useState } from 'react';
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
const onSubmit = (e) => {
e.preventDefault()
saveSettings({ apiKey})
}
return (
<div>
<form className='add-form' onSubmit={onSubmit}>
<div className='form-control'>
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type='submit' value='Save settings' className='mt15' />
</form>
</div>
)
}
export default Settings
It looks like by mistake you have used apiKey in App.js file as your state variable. It should be replaced by settings.
const [settings, setSettings] = React.useState();
The above code would make value={apiKey} work properly for textarea in Settings.js file.
And, then onChange will also start working properly.
UPDATE
In addition to the above mentioned error, in case settings props is undefined in Settings.js, this might cause your code to break at useState. So, instead put a check for settings values in useEffect and then set the value. The code would look like this or you can check the codesandbox link here for working demo.
Settings.js
import { useEffect, useState } from "react";
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState();
useEffect(() => {
if (settings?.values?.apiKey) {
setApiKey(settings.values.apiKey);
}
}, [settings]);
const onSubmit = (e) => {
e.preventDefault();
saveSettings({ apiKey });
};
return (
<div>
<form className="add-form" onSubmit={onSubmit}>
<div className="form-control">
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type="submit" value="Save settings" className="mt15" />
</form>
</div>
);
};
export default Settings;
App.js
const [settings, setSettings] = useState()
const saveSettings = async (settings) => {
setSettings(settings);
}

Dynamically add component in react with hooks

i have 3 components: Form (parent), Picklist and ParagraphBox (children); based on the select of the picklist, i render ParagraphBox and also a "+" button. What i would like to achieve is on the click of the plus button, render another ParagraphBox, just under the first. I would also like the remove functionality.
My ParagraphBox component has a title and a content, and i want to give the adding a progressive number:
e.g Paragraph 1
Content: ....
Paragraph 2
Content: ....
And so on
Here's my ParagraphBox component:
import React, { useState, useEffect } from 'react';
export default function ParagraphBox(props) {
const [paragrafo, setParagrafo] = useState({})
useEffect(() => {
console.log('paragrafo ', paragrafo)
props.onChange(paragrafo)
}, [paragrafo])
const onChange = (e) => {
const titolo = e.target.name
const contenuto = e.target.value
setParagrafo({
...paragrafo,
[titolo]: contenuto
})
}
return (
<div className = "paragraph-box">
<label>
{props.labelInputBox}
<div>
<input type="text" name="titolo" value={paragrafo.titolo || ''} onChange={onChange}/>
</div>
{props.labelTextArea}
<div>
<textarea id="subject" name="contenuto" placeholder="Inserisci contenuto.." style={{height: "45x", width: "400px"}} value={paragrafo.contenuto || ''} onChange={onChange} />
</div>
</label>
</div>
)
}
Here is my Form component:
import React, { useState, useEffect, useRef } from 'react';
import './Form.css'
import createDocument from '../pdfTool';
import finalita from '../icons/finalita.PNG';
import Picklist from './Picklist.js';
import ParagraphBox from './ParagraphBox';
export default function Form() {
const [flagImg, setFlagImg] = useState(false)
const [flagPar, setFlagPar] = useState(false)
const [paragArray, setParagArray] = useState([
{titolo: '', contenuto: ''}
])
const handleChange = (e) => {
console.log('e ', e)
console.log('e.titolo PARENT ', e.titolo)
console.log('e.contenuto PARENT ', e.contenuto)
setParagArray({
...paragArray,
[e.titolo]: e.contenuto
})
}
useEffect(() => {
console.log('rendering useEffect')
console.log('flagPar: ', flagPar)
console.log('flagImg: ', flagImg)
console.log('paragArray ', paragArray)
}, [flagPar, flagImg, paragArray])
const handleSubmit = (evt) => {
evt.preventDefault(); //usato per evitrare il refresh del browser
}
const addParag = (parag) => {
console.log('paragArray PARENT ', paragArray)
}
const onSelect = (selectedValue) => {
console.log('valore selezionato nella picklist: ' + selectedValue)
if(selectedValue === 'Layout 2') {
setFlagImg(true)
setFlagPar(true)
}
}
return(
<div>
<Picklist onSelect={onSelect} label="Seleziona un layout di contratto: " pickVals={["Seleziona...", "Layout 1", "Layout 2", "Layout 3"]}/>
{flagImg ? (
<form onSubmit={handleSubmit}>
<Picklist onSelect={onSelect} label="Seleziona Immagine: " pickVals={["Seleziona...", "Immagine 1", "Immagine 2", "Immagine 3"]} />
</form>
) : null}
{flagPar ? (
<div>
<ParagraphBox labelInputBox="Paragfrafo 1" labelTextArea="Contenuto Paragrafo" onChange={handleChange}/>
<div id = "add-paragraph">
<button type="button" onClick={addParag}>+</button>
<input type="submit" value="Submit" />
</div>
</div>
) : null}
</div>
)
Thanks in advance for your time
I know this is old...but I just faced the same issue, so here it goes: JSX is just syntactic sugar for regular JavaScript. Therefore you can just create the component manually and make it available as part of your hook, i.e.:
custom hook:
import React, { useState } from 'react';
import Advise from '../../components/Advise/Advise';
const useAdvise = () => {
const [ showAdvise, setShowAdvise ] = useState(false)
const [ adviseMsg, setAdviseMsg ] = useState('')
const [ loading, setLoading ] = useState(false)
const hideAdvise = () => {
setShowAdvise(false)
}
const adviseComponent = React.createElement(Advise, {show:showAdvise, showSpinner:loading, hideAdvise:hideAdvise, children:adviseMsg})
return {
adviseComponent,
setShowAdvise,
setAdviseMsg,
setLoading
}
};
export default useAdvise;
component where I want it:
import useAdvise from '../hooks/useAdvise/useAdvise'
const Page = () => {
const {adviseComponent, setShowAdvise, setAdviseMsg, setLoading} = useAdvise()
return(
<div>
{adviseComponent}
</div>
)
}
hope it helps (cheers from Br as well)

How do I return an input tag from a function?

I have a button on my webpage, and I want an input tag to appear, whenever the user clicks that button. I earlier tried something like this:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [showInput, setShowInput] = useState(false);
const handleClick = () => setShowInput(true);
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{showInput ? <input type="text" /> : ""}
</div>
);
}
But this only worked once. I want it to add an input tag whenever the user clicks that button. How do I do so?
Instead of maintaining the number of input elements in the state, i suggest that you maintain an object in the state that is initially empty. Once the button is clicked to add an input, you could update the object with a key-value pair that represents the new input element.
State after adding one input could like as shown below:
{
input1: { value: '' }
}
Similarly, as more inputs are added, more objects will be added in the state.
This will allow your input elements to be controlled components and will allow you to handle the onChange event with only one event handler function.
Demo
let counter = 1;
function App() {
const [inputs, setInputs] = React.useState({});
const handleClick = () => {
const inputName = "input" + counter++;
const inputObj = { value: "" };
setInputs({ ...inputs, [inputName]: inputObj });
};
const handleChange = (event) => {
const { name, value } = event.target;
setInputs({ ...inputs, [name]: { ...inputs[name], value } });
};
return (
<div className="App">
<button onClick={handleClick}>Add Input</button>
<div className="inputContainer">
{Object.keys(inputs).map((inputName) => {
const { value } = inputs[inputName];
return (
<input
key={inputName}
name={inputName}
value={value}
onChange={handleChange}
placeholder={inputName}
/>
);
})}
</div>
</div>
);
}
ReactDOM.render(<App/>, document.querySelector('#root'));
.App {
font-family: sans-serif;
text-align: center;
}
.inputContainer {
display: flex;
flex-direction: column;
max-width: 300px;
margin: 10px auto;
}
input {
margin: 5px;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Make showInput a number that defaults to 0.
Have handleClick increment that number instead of just setting true.
Outside the return expression, create an array. With a for loop, push inputs (until you reach the number specified) into the array.
Replace the line where you add the input to the JSX with that array.
Something like ...
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [inputs, setInputs] = useState([]);
const handleClick = () => setInputs([...inputs, ""]);
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{inputs.map(i => <input type="text"/>)}
</div>
);
}
Now you can also store your input values into your inputs state for further processing.
I leave formatting up to you ... !
import React, { useState } from "react";
export default function App() {
const initialValue = [{ value: "first input" }];
const [userInputs, setUserInputs] = useState(initialValue);
const handleClick = () => {
const updatedInputs = [...userInputs, { value: "new input"}]
setUserInputs(updatedInputs);
}
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{userInputs.map((el, i) => (
<input type="text" value={el.value} />
))}
</div>
);
}
All of the implementation above is correct, But I also have my own implementation.
import React, { useState, Fragment } from "react";
export default function App() {
const [showInputs, setInputs] = useState([]);
const handleClick = () => {
setInputs((prev) => {
const i = prev.length + 1;
return [
...prev,
<Fragment key={i}>
<input type="text" />
<br />
</Fragment>
];
});
};
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
<br />
{showInputs}
</div>
);
}

Resources