pass function to input onChange and then set it in state reactjs? - html

I just cannot figure out how to do this.
This function works to split the string of the input ID.
function func() {
// Original string
let str = urlInput.value
// Splitting up the string
let array = str.split(".com/");
let joined = array[0]+".com/embed/"+array[1];
document.write(joined);
console.log("function happened")
}
I am trying to pass it through onChange and then set it in state but the function isn't being passed onChange?
{
currentAccount ? (<textarea
placeholder={spotifyLink}
type="url"
id="urlInput"
onChange={e => {{func}; setMessageValue(e.target.value)}}/>) : null
}
What am I doing wrong? How do I pass the function and then setState after the function has split the user input string onChange?

You can make use of controlled components. With this approach, the input value is controlled by a state. It promotes single source of truth, you can read more here.
CODE -
function TextAreaComponent() {
const [messageValue, setMessageValue] = useState('');
const splitString = (str) => {
// Splitting up the string
let array = str.split(".com/");
let joined = array[0]+".com/embed/"+array[1];
return joined;
}
const handleOnChange = (event) => {
const { value } = event.target;
const splittedString = splitString(value);
setMessageValue(splittedString);
}
return (
<textarea
// placeholder={spotifyLink}
// id="urlInput"
type="url"
value={messageValue}
onChange={handleOnChange}
/>
);
}

Related

How to set initial value on useRef<HTMLInputElement> - ReactJs + Typescript

I would like to set a number as initial value on useRef<HTMLInputElement>.
I don't need to use useState<number>() because the field is a simple counter.
Here is my typescript code:
const MyComponent = () => {
const productAmountRef = useRef<HTMLInputElement>();
const handleReduceClick = () => {
productAmountRef.current.value -= 1;
}
const handleAddClick = () => {
productAmountRef.current.value += 1;
}
return (
<>
<SomeWrapper>
<ReduceButton onClick={handleReduceClick}/>
<input disabled={true} ref={productAmountRef}/>
<AddButton onClick={handleAddClick}/>
</SomeWrapper>
</>
)
}
For obvious reasons, when the onClick function is triggered, the value is a NaN.
My doubt is, how can I set a Initial Value on useRef<HTMLInputElement>? As I said and as you saw, it need to be a number.
Is this possible?
Set the initial value using the defaultValue attribute:
<input disabled={true} ref={productAmountRef} defaultValue={3} />
Or use useState() and render the number without the use of an input:
const MyComponent = () => {
const [productAmount, setProductAmount] = useState(0);
const handleReduceClick = () => {
setProductAmount(val => val - 1);
}
const handleAddClick = () => {
setProductAmount(val => val + 1);
}
return (
<SomeWrapper>
<ReduceButton onClick={handleReduceClick}/>
<div>{productAmount}</div>
<AddButton onClick={handleAddClick}/>
</SomeWrapper>
)
}

How would you add append to an objects property if it exists conditionally with ES6?

Imagine if there is an object with the following property.
var rules = {
confirmedPassword: 'required|min:4|max:10',
}
What I want is this:
rules = { rules.confirmedPassword && {...rules.confirmedPassword, rules.confirmedPassword: '|same:password'}}
To yield this:
rules = {confirmedPassword: 'required|min:4|max:10|same:password',}
The reason being, in react I am using the event.target in a function to perform a validation later.
So if a user hits the input element with name="confirmPassword" not only will it have the default of required|min:4|max:10 but in fact 'required|min:4|max:10|same:password';
validateInputs(event) {
var { password, confirmPassword } = this.state;
var { name, value } = event.target;
var data = {
password: password,
confirmPassword: confirmPassword
};
var rules = {};
rules[name] = 'required|min:4|max:10';
var messages = {
'password.min': 'Password is too short.',
'password.max': 'Password is too long.',
'confirmPassword.min': 'Password is too short.',
'confirmPassword.max': 'Password is too long.'
};
validate(data, rules, messages)
.then(feedback => console.log(feedback))
.catch(errors => console.log('foo', errors));
this.setState({
[name]: value
});
}
Thanks in advance!
What you can do is to have the properties stored in an array, and then joining them and/or removing them as you go along.
So for example, say you have 4 inputs, you could have the entire component like so:
import React from 'react';
export default class TestComponent extends React.Component {
DEFAULT_RULES = ['required', 'min:4', 'max:10'];
validateInputs(event) {
const { password, confirmPassword } = this.state;
const { name, value } = event.target;
// This can be done this way in ES6, you don't need to do password: password
const data = {
password,
confirmPassword
};
let rules = {};
const rulesArr = this.DEFAULT_RULES.concat('same:password');
rules[name] = rulesArr.join('|');
validate(data, rules, messages)
.then(feedback => console.log(feedback))
.catch(errors => console.log('foo', errors));
this.setState({
[name]: value
});
}
}
I may not have the full context but I hope you get what is going on here

adding elements to json string(array) FLUTTER

I have a JSON string, inside of it I have an array, I want to add data to it as I tried to do it below:
String myJSON = '{"listOfSubtasks":["dasd","dadd","dadsd"]}';
arrayToStringAndBack(addElement) async {
var json = jsonDecode(myJSON);
var getArray = json['listOfSubtasks']; //returns [dasd,dadd,dadsd]
setState(() {
getArray.add(addElement);
});
// as I want to push it to a db I convert [dasd,dadd,dadsd] to a String ["dasd","dadd","dadsd"]
String arrayToString = jsonEncode(getArray);
print(arrayToString);
}
...
textfieldform
- onSaved: (val) {
subtasks = val;
arrayToStringAndBack(val);
},
...
When I type smth and click on a submit button an element is added to the end of an array but once I try to do it one more time, to add an element, the last element that was added changes to one I created.
I want to add as many elements as I want, not just a single one
Solved
var arrayOfSubTasks = [];
arrayToStringAndBack(addElement, arr) async {
var json = jsonDecode(myJSON);
var getArray = json['listOfSubtasks'];
setState(() {
getArray.add(arr);
});
String arrayToString = jsonEncode(getArray);
print(arrayToString);
}
...
onSaved: (val) {
subtasks = val;
setState(() {
arrayOfSubTasks.add(val);
});
arrayToStringAndBack(val, arrayOfSubTasks);
},
You can treat your list of subtasks as a List to be able to add String with List.add(). Then encode the List to a json with jsonEncode()

Get the type of generic function without invoking the function in typescript

I have a generic function like this:
function foo<T>(val: T) { return val; }
I have some types:
type StringType = string;
type NumType = number;
Now I want to make a reference to the 'foo' function with a given type, but it wont work:
const stringFunc = foo<StringType>;
const numFunc = foo<NumType>;
Note that I don't want to invoke the 'foo' function, otherwise, I could do:
const stringFunc = (val: string) => foo<StringType>(val);
const numFunc = (val: number) => foo<NumType>(val);
Is it possible?
If you just want to force the type and don't mind extra typing you can do it like this:
const stringFunc: (val: string) => string = foo;
const numFunc: (val: number) => number = foo;
But generally applying the type parameter to a generic function is not possible at the moment.
What about type casting or invoking some other adhoc generic function?
TypeScript REPL
function foo<T>(v: T) { return v }
type UnaryFunction<T> = (v: T) => T
// casting
const asStrFoo = foo as UnaryFunction<string> // asStrFoo(v: string): string
const asNumFoo = foo as UnaryFunction<number> // asNumFoo(v: number): number
// identity function
const bind = <T>(f: UnaryFunction<any>): UnaryFunction<T> => f
const strFoo = bind<string>(foo) // strFoo(v: string): string
const numFoo = bind<number>(foo) // numFoo(v: number): number
// function factory
function hoo<T>() { return function foo(v: T) { return v } }
// function hoo<T>() { return (v: T) => v }
const strHoo = hoo<string>() // strHoo(v: string): string
const numHoo = hoo<number>() // numHoo(v: number): number
note
// This condition will always return 'false' since the types 'UnaryFunction<string>' and 'UnaryFunction<number>' have no overlap.
console.log(strFoo === numFoo) // true
// This condition will always return 'false' since the types 'UnaryFunction<string>' and 'UnaryFunction<number>' have no overlap.
console.log(asStrFoo === asNumFoo) // true
// This condition will always return 'false' since the types '(v: string) => string' and '(v: number) => number' have no overlap.
console.log(strHoo === numHoo) // false

Redux, Fetch and where to use .map

Consider this scenario:
app loads => fetches json from api => needs to modify json returned
In this case, I'm using moment to make some date modifications and do some grouping that I'll use in the UI. I looked on stack and found a similar question but didn't feel like it provided the clarity I am seeking.
Where should I use .map to create the new objects that contain the formatted & grouped dates? Should I manipulate the raw json in the api call or in the redux action before I dispatch? What is the best practice?
Is it OK to add properties and mutate the object as I am showing below,
service["mStartDate"] = mStartDate before I put the data into my store and treat it as immutable state?
First Approach - changing raw json in the api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
})
}
}
2nd Approach, make a simple api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
}
}
Change the json in the action before dispatching
export function getDataByID() {
return (dispatch, getState) => {
dispatch(dataLookupRequest())
const state = getState()
const groupedData = {}
return TicketRepository.getDataByID(userData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
return groupedData
})
.then(groupedData => {
dispatch(lookupSuccess(groupedData))
})
.catch(err => dispatch(dataLookupFailure(err.code, err.message)))
}
}
All data manipulation should be handled by your reducer. That is, the returned response data should be passed on to a reducer. This practice is common, because this way if there's a problem with your data, you will always know where to look - reducer. So neither of your approaches is "correct". Actions should just take some input and dispatch an object (no data manipulation).
When you want to manipulate data for 'view' purposes only, consider using reselect library, which makes it easier to handle "data views" that are composed of the existing data.