I'm trying to send data from Child component to parent component using call back function.
This is my parent component
const handleSubmit = async (e) => {
e.preventDefault()
try {
const response = await promiseApp.post('/promises', {
id: "",
uuid: "",
content: promise,
date: date,
time: time,
place: place,
phone_number: phone
})
addPromise(response.data.promise)
history.push('/promises')
} catch (err) {
alert("An error has occured")
console.log(err)
console.log('zzzzzzzz')
}
}
const updateNumbersInParent = (number) => {
console.log(number)
console.log('hhhhhhhhhh')
let value = number.map((element) => {
return element.newNumber
})
console.log(value)
for (let i = 0; i < value.length; i++) {
console.log(value[i])
setPhone(value[i])
}
}
return (
<div className="tile is-parent">
<article className="tile is-child box">
<PhoneNumber phone={phone} setPhone={setPhone} onChange={value => setPhone(value)}
updateNumbersInParent={updateNumbersInParent}/>
</article>
</div>
)
This is my child component
function PhoneNumber (props) {
// const [count, setCount] = useState(1)
const [phoneNumber, setPhoneNumber] = useState([])
const addNumberButton = (e) => {
e.preventDefault()
addPhoneNumber(props.phone)
props.setPhone('')
}
const addPhoneNumber = (newNumber) => {
const addNewPhoneNumber = [...phoneNumber, {newNumber}]
setPhoneNumber(addNewPhoneNumber)
props.updateNumbersInParent(addNewPhoneNumber)
}
const deletePhoneNumber = (number) => {
const filteredPhoneNumbers = phoneNumber.filter(currentPhoneNumbers => (currentPhoneNumbers !== number))
setPhoneNumber(filteredPhoneNumbers)
props.updateNumbersInParent(filteredPhoneNumbers)
}
return (
<>
<p className="title">Type a phone number and click add</p>
<div className="field">
<div className="control">
<button className="button is-primary" onClick={addNumberButton}>Add</button>
<p><b>No. of phone numbers:</b></p>
<input className="input is-danger" pattern='^\+[1-9]\d{1,14}$' value={props.phone} onChange={e => props.onChange(e.target.value)} type="tel" placeholder="Enter your phone number here"></input>
<p>(Format: +10000000000)</p>
</div>
<ul>
{phoneNumber.map((number, index) => {
return (
<>
<li key={index}>{number.newNumber}</li>
<button onClick={() => deletePhoneNumber(number)}>Delete</button>
</>
)
})}
</ul>
</div>
</>
)
}
I can see that the data from Child component has been passed to the Parent component but keep getting an error when I do POST request. phone_number property in the parent component has an empty value every time I do the POST request.
Related
My question is how can I send the input value to the parent component by clicking on the button? Because now if I type something in the input it shanges the value instantly, I want it to do after I click on the button.
Currently I am using that method:
const FormInput = ({setIpAddress}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" onChange={(e) => setIpAddress(e.target.value)} required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn">
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
export default FormInput
You can pass an onClick callback function to the child component. When this function is called it will trigger a rerender in the child.
Example:
Parent:
const handleClick = (value) => {
//set the state here
}
<ChildComponent onClick={handleClick} />
Child:
<button type="submit" className="input_btn" onClick={(value) => props.onClick?.(value)}>
In your case you need to get rid of the onChange in your input tag:
parents:
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
const handleClick = (event) => {
setIpAddress(event.target.value)
}
return (
<div className="App">
<SearchSection onClick={handleClick} />
</div>
);
}
const SearchSection = ({onClick}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput onClick={onClick}/>
</div>
);
};
Child
const FormInput = ({onClick}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn" onClick={(e) => onClick(e}>
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
Thank you for your answer, but I don't really get it, bcs my parent component has no paramter, sorry I am new in react.
This is my parent component where I am fetching the data and I want to update the ipAdress when I click on the button which is in the FormInput component. So the SearchSection is the parent of the FormInput.
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip)
...
getData();
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}
I hope it's enough :)
const SearchSection = ({setIpAddress}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput setIpAddress={setIpAddress}/>
</div>
);
};
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}
function DataWeather({ weather, setWeather }) {
const cityName = weather.city.name;
const countryName = weather.city.country;
const minTemp = weather.list.main.temp_min;
const maxTemp = weather.list.main.temp_max;
const handleRemoveItem = (id) => {
setWeather((weather) => weather.filter((city) => city.id !== id));
};
return (
<div>
<div>
<span onClick={handleRemoveItem}>
x
</span>
<p>
{cityName} {countryName}
</p>
<p>MaxTemp : {maxTemp}</p>
<p>MinTemp: {minTemp}</p>
</div>
</div>
);
}
export default DataWeather;
Hey, I'm trying to remove city from coming api, but i get error that filter is not a function. Anyone has idea why I get this error. I have initialize weather with useState([]).
function WeatherData() {
const [query, setQuery] = useState("");
const [weather, setWeather] = useState({});
const FetchData = async (e) => {
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/forecast?q=${query},&appid=${YOU_API}`
);
const weatherData = await response.json();
setWeather(weatherData);
} catch (err) {
console.log(err);
}
};
return (
<div>
<main>
<form onSubmit={FetchData}>
<input
type="text"
placeholder="Click"
onChange={(e) => setQuery(e.target.value)}
value={query}
/>
{Object.entries(weather).length !== 0 ? (
<DataProfile weather={weather} setWeather={setWeather} />
) : (
<h3> Please City Name </h3>
)}
<button className="btn">Click</button>
</form>
</main>
</div>
);
}
as you can see here is the rest of my code fetching the weather api to get the detail of this api. I just want to remove the data by clicking the X.
Because weather is an object so you can't delete it with filter.
You can use spread operator to update object like this:
const handleRemoveItem = (id) => {
setWeather((weather) => ({...weather, city: {}}));
};
Hi i am working on a React application where there are four options.when a user select an option corresponding input element will be added to the wrapper.In the following code add operation works fine but remove operation is not working properly ,it is not removing the corresponding element.Another problem the values on the inputs fields not present when the component re-renders.so experts guide me how i can acheive removing the corresponding row when the remove button is clicked and the input values should not be reset when the component re-renders.
But when I submit the input it will appear my data perfectly and when i restart the page and just click into edit and hit submit with the defaultValue it just clear all the data and send back to my backend with undefined value like this: [ undefined, undefined, undefined, undefined ]
Here is my full component:
const Agreement = (props) => {
const { agreement, editable, teamData, teamId, fetchTeamData } = props;
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [showErrors, setShowErrors] = useState(false);
const [errorsArr, setErrorsArr] = useState();
const initialFormState = {
rule_0: teamData.rules.rule_0,
rule_1: teamData.rules.rule_1,
rule_2: teamData.rules.rule_2,
rule_3: teamData.rules.rule_3,
creator: teamData.User.public_user_id,
};
const [updateTeamData, setUpdateTeamData] = useState(initialFormState);
const [inputs, setInputs] = useState(teamData.rules);
const handleChange = (event) => {
const { name, value } = event.target;
// Update state
setUpdateTeamData((prevState) => ({
...prevState,
[name]: value,
}));
};
// Add more input
const addInputs = () => {
setInputs([...inputs, { name: `rule_${inputs.length + 1}` }]);
};
// handle click event of the Remove button
const removeInputs = (index) => {
const list = [...inputs];
list.splice(index, 1);
setInputs(list);
};
const clearInput = (dataName) => {
setUpdateTeamData((prevState) => {
delete prevState[dataName];
return {
...prevState,
};
});
};
const handleSubmit = async (event) => {
event.preventDefault();
setEditing(false);
// Send update request
const res = await axios.put(`/api/v1/teams/team/${teamId}`, updateTeamData);
// If no validation errors were found
// Validation errors don't throw errors, it returns an array to display.
if (res.data.validationErrors === undefined) {
// Clear any errors
setErrorsArr([]);
// Hide the errors component
setShowErrors(false);
// Call update profiles on parent
fetchTeamData();
} else {
// Set errors
setErrorsArr(res.data.validationErrors.errors);
// Show the errors component
setShowErrors(true);
}
};
const handleCancel = () => {
setEditing(false);
};
useEffect(() => {
if (agreement === "default") {
setTitle(defaultTitle);
setInputs(teamData.rules);
} else {
setTitle(agreement.title ?? "");
}
}, [agreement, teamData]);
console.log("teamData.rules", teamData.rules);
console.log("inputs", inputs);
return (
<div className="team-agreement-container">
{!editing && (
<>
<h4 className="team-agreement-rules-title">{title}</h4>
{editable && (
<div className="team-agreement-rules">
<EditOutlined
className="team-agreement-rules-edit-icon"
onClick={() => setEditing(true)}
/>
</div>
)}
<p className="team-agreement-rules-description">{description}</p>
{teamData.rules.map((rule, index) => (
<div className="team-agreement-rule-item" key={`rule-${index}`}>
{rule ? (
<div>
<h4 className="team-agreement-rule-item-title">
{`Rule #${index + 1}`}
</h4>
<p className="team-agreement-rule-item-description">
- {rule}
</p>
</div>
) : (
""
)}
</div>
))}
</>
)}
{/* Edit rules form */}
{editing && (
<div className="team-agreement-form">
{showErrors && <ModalErrorHandler errorsArr={errorsArr} />}
<h1>Rules</h1>
{inputs.map((data, idx) => {
return (
<div className="agreement-form-grid" key={`${data}-${idx}`}>
<button
type="button"
className="agreement-remove-button"
onClick={() => {
removeInputs(idx);
clearInput(`rule_${idx}`);
}}
>
<Remove />
</button>
<input
name={`rule_${idx}`}
onChange={handleChange}
value={teamData.rules[idx]}
/>
</div>
);
})}
{inputs.length < 4 && (
<div className="team-agreement-add-rule">
<button type="submit" onClick={addInputs}>
<Add />
</button>
</div>
)}
<div className="div-button">
<button className="save-button" onClick={handleSubmit}>
Save
</button>
<button className="cancel-button" onClick={handleCancel}>
Cancel
</button>
</div>
</div>
)}
</div>
);
};
export default Agreement;
How can I fix this error?
My thought is the problem is around [inputs, setInputs]
Try this
<input
//..
onChange={(event) => handleChange(event.target.value)}
//..
/>
then in your "handleChange" function
const handleChange = (event) => {
const { name, value } = event;
//....
};
I am working on creating a clear button that once clicked will clear all the transactions that have been added to the transaction list with localStorage. My button works but its buggy, once it gets clicked I get the following error about a separate function I have to get the balance. If I refresh the page afterwords though all the transactions will be cleared.
The error I am receiving ...
TypeError: amounts.reduce(...).toFixed is not a function
my component
import react, {useState, useEffect} from 'react'
import Transaction from './Transaction'
const Form = () => {
//initial state
const [transaction, setTransaction] = useState({
description: '',
amount: ''
})
const [list, setList] = useState(
JSON.parse(localStorage.getItem('list')) || []
)
const [balance, setBalance] = useState('')
const [income, setIncome] = useState(
JSON.parse(localStorage.getItem('income'))
)
const [expense, setExpense] = useState(JSON.parse(localStorage.getItem('expense')))
//updates based onChange value
const updateBalance = (e) => {
setTransaction({
...transaction,
[e.target.name]:
e.target.type == 'number' ? parseInt(e.target.value) : e.target.value
})
}
//identify if transaction is income/expense
const plusMinus = () => {
transaction.amount > 0
? setIncome(income + transaction.amount)
: setExpense(expense + transaction.amount)
}
// updates balance after transaction is added
const getBalance = () => {
const amounts = list.map(i => i.amount);
const money = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
setBalance(money)
}
useEffect(() => {
getBalance()
localStorage.setItem('list', JSON.stringify(list))
localStorage.setItem('income', JSON.stringify(income))
localStorage.setItem('expense', JSON.stringify(expense))
}, [list])
//clear transaction list
const clearBudget = () => {
localStorage.clear();
}
const onSubmit = e => {
e.preventDefault();
setList([transaction, ...list])
plusMinus()
setTransaction({ description: '', amount: ''})
}
return (
<div>
<div className='totals'>
<h2 className='balance'> Current Balance </h2>
<h3> ${balance} </h3>
<h4> Income: ${income} Expense: ${expense} </h4>
</div>
< br />
< br />
< br />
<h2 className='trans-history'> Transaction History </h2>
{list.map(i => {
return (
<div className='trans'>
<ul key={i.description}>
{i.description} ${parseInt(i.amount)}
</ul>
</div>
)
})}
<br />
<br />
<h2 className='enter-item'> Enter an Item </h2>
<form onSubmit={onSubmit}>
<div>
<input
type='text'
className="input-trans"
placeholder='Enter Transaction'
value={Transaction.description}
name='description'
onChange={updateBalance}
>
</input>
</div>
<div>
<input
type='number'
className='input-trans'
placeholder='Enter Amount'
name='amount'
value={transaction.amount}
onChange={updateBalance}
>
</input>
</div>
<br/>
<div className='button-container'>
<button type='submit' className='button is-primary'> Submit </button>
<button className='button is-danger' onClick={clearBudget}> Clear </button>
</div>
</form>
</div>
)
}
export default Form
Looks like amounts.reduce is returning something that is not a number. You could check the type before to perform toFixed function.
E.g.:
const amounts = list.map((i) => i.amount).map(Number);
const money = amounts.reduce((acc, item) => (acc += item), 0)
if (typeof money === 'number') {
setBalance(money.toFixed(2))
} else {
setBalance(money)
}
I am creating a CommentBox.
I am trying to refresh the comment list after form submission.
#CommentBox.js
const CommentBox = (props) => {
return (
<div className="comment_area clearfix mb-5">
<div className="section-heading style-2 mb-5">
<h4>Comment</h4>
<div className="line"></div>
</div>
< CommentForm />
< CommentList />
</div>
);
}
As you can see I have different components for CommentForm.js and CommentList.js
#CommentForm.js
const onSubmitHandler = (e) => {
........
axios.post(......................)
................................
}
return (
<form onSubmit={onSubmitHandler}>
|
|
</form>
);
#CommentList.js
useEffect(() => {
const id = props.postId;
const fetchData = async () => {
try {
const res = await axios.get(
`.......................`
)
setComments(res.data.results);
} catch (err) {}
};
fetchData();
}, [props.postId])
return (
.................
.................
......
)
How should I write the GET Method in form onsubmitHandler().
Or I have to change some other things to make it work.
A way to solve this is to move the state to your parent component, that is, make the axio calls to the parent component CommentBox. The CommentForm notifies the parent via callback that the form has been submitted and then you link one axios call after the other, passing the GET results to CommentList.
const CommentBox = (props) => {
const [comments, setComments] = useState([]);
const onSubmitHandler = (e) => {
........
axios.post(.......)
.then(() => axios.get())
.then((res) => setComments(res.data);
}
return (
<div className="comment_area clearfix mb-5">
<div className="section-heading style-2 mb-5">
<h4>Comment</h4>
<div className="line"></div>
</div>
< CommentForm onSubmit={onSubmitHandler}/>
< CommentList comments={comments}/>
</div>
);
}