How can I set customvalidity to have a Link in it? - reactjs

I have a customvalidity function for when a username or email are taken, and I want to give the user the option to go to signin from there. Is this possible?
I have tried creating an object for the Link object, from react-router dom, but it eiter doesn't come up when added with a comma, or comes back object object when inserted with a plus sign. I want the email taken notification to have the link in it so the user can click on login directly.
handleSubmit = async (e) => {
e.preventDefault();
const EmailField = document.getElementById('email');
const userField = document.getElementById('username');
const signinRedirect = <Link to='/signin'> login </Link>;
const newUser = {
username : this.state.username,
email : this.state.email,
password : this.state.password,
confirmPassword : this.state.confirmPassword,
};
//check passwords match before submitting
if (this.state.passwordsMatch) {
let url = window.location.hostname.split('.')[0] === 'localhost' ? 'http://localhost:3306' : 'https://petsosbackend.apps.nonprod-mpn.ro11.allstate.com';
try {
await axios.post(url + '/register', newUser)
.then(res=> {
if (res.status===202) {
this.props.history.push('/registersuccess');
//if email is taken
} else if (res.data==='email'){
EmailField.setCustomValidity('Email address is in use, please select another or '+ {signinRedirect});
EmailField.reportValidity();
//if name is taken
} else if (res.data==='name') {
userField.setCustomValidity('Username is in use, please select another or' + signinRedirect);
userField.reportValidity();
}
})
} catch(error) {
console.log("Catch = ", error.response);
}
}
}
There is more logic obviously but think these are the two key parts and just need help figuring out how to have the signinRedirect appear in the validity warning.
Thanks a lot!

Principle:
return (
<div className="form-control">
<input type="text" name="email" value={email} onChange={handleChange} />
{isEmailInUse && (
<div className="error">
Email address is in use, please select another or <Link to='/signin'>Sign in</Link>
</div>
)}
</div>
);
You are trying to use native messages in a JSX validation method. So you can not do.
JSX has nothing to do with HTML, besides being a way of describing the structure of a document and a similar syntax.
JSX In Depth
Just do not use native error reporting. Use your own solutions, like the ones I gave in the answer.
You are also working incorrectly with DOM elements. In React, it is common practice to use Ref API or monitored components.
And whenever possible, there should be no conditions in the code like:
window.location.hostname.split('.')[0] === 'localhost'
For such tasks there are env variables and configurations. You can set BASE_URL and use different values for different modes.

Related

How to condition a component to display certain information?

const handleChecked=(e)=>{
setForm({
...form,
[e.target.name]:e.target.checked
})
if(e.target.name == 'lowercase'){
showMinus(longitud)
}
}
const handleSubmit =(e)=> {
updatePassword(longitud)
activeLetters()
e.preventDefault();
}
file 1
const ShowPass =()=>{
let charactersLength = characters.length;
let allChar = ''
for(let i = 0; i<range.current.value; i++){
allChar += characters.charAt(Math.floor(Math.random()* charactersLength))
}
setPassword(allChar)
}
const showMinus=(minusculas)=>{
let allMin=''
let minLong = minusculas.length;
for(let i = 0; i<range.current.value; i++){
allMin += minusculas.charAt(Math.floor(Math.random()* minLong))
}
setMinus(allMin)
}
//¿Que tipo de carcarter es?
const activeLetters =()=>{
let min = ''
for(let i=0;i<characters.length;i++){
let element = characters[i];
if(element === element.toLowerCase() && element !== element.toUpperCase())
min+=element
}
showMinus(min)
}
useEffect(()=>{
getPassLength()
},[])
return(
<>
<div className='classHeader'>
<ShowPassword password={password} icon={<HiOutlineClipboardList/>} />
</div>
<div className='longBox'>
<p>Character Length</p>
<input type="range" min={1} max={16} step={1} ref={range} name="password" ></input>
{long}
</div>
<CapitalLetters
updatePassword={ShowPass}
showMinus={showMinus}
activeLetters={activeLetters}
longitud={long} />
</>
)
Hello everyone, I would like your help please: I am in a situation with the code, what I want to do is the following:
It is a password generator when I give it send
this generates a random password for me when I click the submit button, for this I have created an external component called showPassword that receives a prop called password So, so far so good because I manage to do that, my problem comes in the form file because I have some checkboxes, I want that when push lowercase it sends the password to the screen in only lowercase, that's where my problem is I don't know how to do it,
how do you think i should do it?
So on checkbox click use a state to see whether the ckeckbox is clicked on not. If yes there are js function to convert string to lowercase.
The toLowerCase() method converts a string to lower case letters.
Create a state that will control whether the checkbox is checked or not then inside the props you can create a ternary operator that will send a lowercase if checked or a normal password if not.
<ShowPassword password={isChecked ? password.toLowerCase() : password} icon={<HiOutlineClipboardList/>} />
Hope this works!

Ternary operator in Typescript based Reactjs does not work as I expected

I am newbie in TypeScript+ReactJS
In my code Ternary operator does not work as I expect.
This is my code:
import React, { SyntheticEvent,useRef,useState } from "react";
import Result from './Result';
//main application
const App:React.FC=()=>{
const [searchResult,setSearchResultState]=useState<Array<Object>>([]);
const setSearchResultFunc = (value:Array<Object>)=>{
setSearchResultState(value);
}
return (
<section id="App">
<SearchPanel setResult={setSearchResultFunc}/>
{
(searchResult===[])
?""
:<Result result={searchResult}/>}
<footer className={searchResult===[]?"absolute":""}>
<p>All data are received using Google Place API. {"result: " +searchResult}
<br/>
This website is not actual working website and is made just for educational purpose.
</p>
</footer>
</section>
)
}
export default App;
interface searchProps{
setResult:(value: Array<Object>) => void
}
const SearchPanel=({setResult}:searchProps)=>{
const searchElementRef = useRef(null as any);
const search =(e:SyntheticEvent)=>{
const searchValue = searchElementRef.current.value;
if(typeof searchValue!=="string"){
alert("only text allowed");
}else{
//validate user search input
regexValidate(searchValue)
?alert("No special characters are allowed")
//send request to server if no special character is found
:fetch("//localhost:3000/city?name="+searchValue,{
method:'GET',
credentials:'include',
headers:{
'Accept':'application/json',
'Content-Type':'application/json'
}
}).then((data:any)=>{
return data.json();
}).then(data=>{
setResult(Object.entries(data));
})
}
e.preventDefault();
}
const regexValidate = (value:string)=>{
const specialChars = /[`!##$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/;
return specialChars.test(value);
}
return(
<section className="text-center absoluteCenter">
<h1 className="text-white text-lg uppercase mb-5">Wanna travel somewhere?</h1>
<form action="/" method="GET" onSubmit={e=>search(e)}>
<input type="text" placeholder="SEARCH FOR CITY ex:London" ref={searchElementRef}/>
</form>
</section>
)
}
So, what I expect is, when application starts, the App component will have an empty Array state which is called searchResultFunc. And when user performs a search, it will update the state by calling function setResultState. The function will have data that search has returned and will alter previous state data to the new data.
According to this ternary operator,
{
(searchResult===[])
?""
:<Result result={searchResult}/>
}
I thought the page should not have Result component from beginning, but when I render the page for first time,
I already see Result in HTML inspector which means ternary operator does not work as I expected.
To check the value of state when the page renders for first time, I made such code
<p>All data are received using Google Place API. {"result: " +searchResult}
<br/>
This website is not actual working website and is made just for educational purpose.
</p>
If state searchResult has some data, the webpage should show value of the state. However, the result state is empty.
What did I do wrong?
you need to change searchResult===[] to searchResult.length === 0
When you compare arrays using === you are actually checking whether the two arrays are the same array. It does not check whether the two arrays have the same content.
Here's a short program that illustrates the difference: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgLhtATgSzAcwNoF0YC8MeAUKJLAEZKoY75GnnQzA1TpZ6Hwl-MgANgFMAdIJCYAFAHI4BApQD8MgDTwFlAJRlwEIWInS5C4CvXyCwHXyA
You need to check whether array is empty or not. You can do the following.
arr=[]
arr.length === 0? True : False
arr===[] does not work as it checks the array reference whether both the array points toward same memory reference. Hence it will fail everytime even if elements are same in both array unless and until it's same array referenced.

compare an object inside a map function React

I try to render a mapped list of activity based on props. Layout of those props are:
completedActivity:
message:
status:
time:
type:
progressId:
time:
userId:
I need to compare the completedActivity.progressId to another set of props.
logData:
timestamp:
taskRunId:
userName:
I need to see where completedActivity.progressId = logData.taskRunId. If they are the same I need to grab the logData.userName.
Here is the code to render out the activities. It is working just, but I need to add additional info to them. As of right now it will render activity.userId, which is a bind user and not the actual user. If they are not the same, it needs to render the bind user.
const ActivityList = props => {
const activityList = props.data.completedActivity.map(activity => (
<li
class="collection-item"
onClick={() =>
activity.messageDetails
? props.toggleMessageDetails(true, activity.messageDetails)
: false
}
>
<p>{activity.type}</p>
<p className="message">{activity.message}</p>
{/*
displays who last ran that activity and when they did. Need to come up with a better naming solution.
*/}
<div class="whodiditandwhen">
<span>{activity.userId}
</span>
{/*<span>{activity.userId}</span>*/}
<span>{new Date(activity.time).toLocaleString()}</span>
</div>
{/*
this will allow the user to click on the icon and see more detailed info on the activity. NOT ALL ACTIVITES HAVE THIS
*/}
{activity.messageDetails}
</li>
));
return (
<ul className="activity-list">{activityList}
</ul>
);};
Here is a screenshot of how it is rendered now.
Thank you
This is what I tried:
const userNameLog = props.data.completedActivity.map(activity => {
let result = props.logData.find(log => log.taskRunID === activity.progressId)
let userName = ""
if(result === undefined){
userName = activity.userId
} else {
userName = result
}
console.log(userName)
}
)
This works to some degree except it gets rendered multiple times.
I was able to solve this issue inside of the axios call that is getting the data before the page renders it.
export function getActivityUpdates(options, updateProgress) {
axios
.post(URL, qs.stringify(options))
.then(response => {
// Null out saved currentActivityID if no live activity.
handleCurrentActivityStorage(response.data.liveActivity);
let changedIDs = [];
let dataResponse = response.data;
dataResponse.userId = ". . ."
/*
iterate over the completed activity and the log data.
compare progress id to taskRunId.
logData is from highest to lowest id.
completedActivity is also from high to low but puts parents above children.
check and make sure that it has a bind user written to it.
Then if taskrunid is = to or less than the progressId. change it and add it to the changedIDs array.
the ChangedIds array makes sure that ids are not iterated over multiple times.
set the userID to the actual username found in the logs.
*/
dataResponse.completedActivity.forEach(data => {
options.logData.forEach(log => {
//
if (
data.userId === options.bindUsername &&
!changedIDs.includes(data.progressId) &&
(log.taskRunID === data.progressId ||
log.taskRunID < data.progressId)) {
changedIDs.push(data.progressId)
data.userId = log.magUserName;
}
})
});
updateProgress(dataResponse);
// Exit function if a task is not running
if (!response.data.liveActivity.length) {
return;
}
// Get updated data every second until task is complete
setTimeout(
getActivityUpdates.bind(null, options, updateProgress),
1000
);
})
.catch(() => showErrorMessage(options.command));
}

react address lookup issue with changing final input value

I am building an address lookup functionality for an app, and my previous 2 questions have thus far, been unable to garner an answer. I am on the last step, and need to figure this out, so I turn to you stack overflow.
I start with an input, a button and a target input:
<input type='text' name='postcode-lookup' onChange={this.postcodeChange} />
<button onClick={this.searchPostcode}>Search</button>
<input type='text' name='target-for-data' />
Easy enough. Now for the functions attached to both of those elements:
postcodeChange = {e} => {
this.setState({'postcode': e.target.value});
}
searchPostcode = () => {
this.setState({'visible': true});
if(this.state.postcode.length . 0){
Axios.get('postcode look up api here')
.then(response => {
this.setState({'response': response.data});
})
}
}
Ok here, we have 3 state items: postcode, which we will set to an empty string '', visible, which is initially set to true, and response, which is an empty array, that we then populate with the response data of address objects.
My next step, was to display those addresses, so inside the render, I set a const that maps over the response array like so:
const result = this.state.response.map((item) => {
if(this.state.visible === true){
return(
<p key={item.id} onClick={addressClick}>{item.address1Field}</p>
)
}else {
}
})
Ok, so when we click the button, it will return a p tag filled with address data from the array. This also has a function, which is where my problem lies.
Inside of this function I set the visible state item to false, so that the addresses disappear. Easy enough.
But how do I then take the address that I clicked on, and populate it into the original input we started with?
I have tried many things, from setting the state in addressClick, targeting the innerHTML, e.target.value, e.target.innerHTML, and so on and so on for hours.
Any takers? Ideas?

React form validation still adds values

So I have a little bit of form validation going on and I am running into an issue. When I first load the web app up and try adding a value and submitting with my button it doesn't allow me and gives me the error I want to see. However, when I add a value setState occurs and then my value is pushed to UI and I try to add another blank value it works and my conditional logic of checking for an empty string before doesn't not go through what am I doing wrong?
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
}, () => {
document.getElementById('test').value = '';
})
console.log(this.state.id);
}
}
You are checking this.state.input but no where in that code are you setting the input value on the state.
Try adding this where it makes sense in your application:
this.setState({ input: 'some value' });
Also, I recommend you use the state to define the application UI. So instead of using document.getElementById('error') or document.getElementById('test').value, have the UI reflect what you have in your state.
See here for more info: https://reactjs.org/docs/forms.html
Instead of manipulating the DOM directly:
document.getElementById('test').value = '';
you'll want to use React:
this.setState({ input: '' });
A good ground rule for React is to not manipulate the DOM directly through calls like element.value = value or element.style.color = 'red'. This is what React (& setState) is for. Read more about this on reactjs.org.
Before you look for the solution of your issue, I noticed that you are directly updating the DOM
Examples
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
document.getElementById('test').value = '';
Unless you have special use case or dealing with external plugins this isn't recommended, when dealing with React you should update using the virtual DOM. https://www.codecademy.com/articles/react-virtual-dom
Pseudo code sample
constructor(props) {
this.state = {
// retain previous states in here removed for example simplicity
errorString: ''
}
}
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
this.setState({
errorString: 'Please enter something first'
});
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
input: '',
});
}
}
// notice the "error" and "test" id this could be omitted I just added this for your reference since you mentioned those in your example.
render() {
return (
<div>
{(this.state.errorString !== '') ? <div id="error" style={{color: 'red'}}>{this.state.errorString}</div> : null}
<input id="test" value={this.state.input} />
</div>
}
Every time you invoke setState React will call render with the updated state this is the summary of what is happening but there are lot of things going behind setState including the involvement of Virtual DOM.

Resources