I am new to React Native and was trying to make some experiences with hooks. In my app I want to refresh a wifi name when I click on a button. I know this can be done without using the refresh button, but it goes against what I am trying to learn.
The problem here is that when I click the button to refresh the variable unreloaded_netInfo, it does change, because console.log shows the changes, but the value in the Text element does not. What am I doing wrong?
Thanks for any help.
I have this code
<View style={styles.container}>
<Text>Wifi name: {unreloaded_netInfo.details.ssid}</Text>
<Button onPress={reloadWifiName} title="refresh" color='#042417'/>
</View>
reloadWifiName:
const netInfo = NetInfo.useNetInfo();
const [unreloaded_netInfo, setNetInfoReload] = useState(netInfo);
const reloadWifiName = () => {
let aux=unreloaded_netInfo;
if(netInfo.type=="wifi")
{
aux.details.ssid=netInfo.details.ssid;
setNetInfoReload(aux);
}
else setNetInfoReload(netInfo);
}
When you use setNetInfoReload(), React only does a shallow comparison i.e == instead of ===, in order to decide whether to re-render or not. You should try to clone the aux object and update setNetInfoReload() with the cloned object
You could like this to declare one metaData variable
const [metaData, setMetaData] = useState(false).
When your state data update at that time also please update metaData value.
setNetInfoReload(aux)
setMetaData(! metaData)
Related
In my Reactjs app, I'm using the payment intent API from stripe to handle payments. I use the embeddable Payment Element <PaymentElement /> from #stripe/react-stripe-js to render the UI but the problem is that it takes a couple of seconds before the Payment Element is fully loaded in the UI.
Is there any way I can access its loading state and show a loading spinner while it's being loaded?
Stripe just added a new loader option to their PaymentElement product documented here. It allows you to have a skeleton render first while the UI is loading which should solve the problem you were going for.
Alternatively, you can listen to their ready event documented here so that you show a loading animation until the event is fired.
While this is for their vanilla JS integration, you can use those with their React library since you control which option to pass on the PaymentElement on initialization.
For anyone not content with simply using their loader, you can listen to the ready event (docs).
To do this, you have to get the element first, which is a step that confused me. You should have the elements reference from the useElements hook. In useEffect you can try to do elements.getElement('paymentMethod') but you will get an error saying:
A valid Element name must be provided. Valid Elements are: card,
cardNumber, cardExpiry, cardCvc, postalCode, paymentRequestButton,
iban, idealBank, p24Bank, auBankAccount, fpxBank, affirmMessage,
afterpayClearpayMessage; you passed: paymentElement.
However, the correct thing to get is payment despite not being in that list:
const element = elements.getElement('payment')
element.on('ready', () => {
console.log("READY")
})
Thanks #Yeats on ready solved this for me - great answer.
I want to add for anyone looking at this solution that you should not hide your until the on ready state returns. I mistakenly used a useState variable to show a loader until PaymentElement was ready, but realised that only the loader component needed to be toggled by state and that the PaymentElement should be rendered always. My first try I hid the PaymentElement from render using my loading state var like this:
Don't do this!
{isStripLoading ? (
<MyLoaderComponent />
) : (
<PaymentElement />
)}
So, assuming you have a state variable isStripeLoading default to true and you have useEffect on ready event setIsStripeLoading(false) then wrap only you loader spinner component in the isStripeLoading state variable and NOT the PaymentElement component.
Example
const stripe = useStripe()
const elements = useElements()
const [isStripeLoading, setIsStripLoading] = useState(true)
useEffect(() => {
if (elements) {
const element = elements.getElement('payment')
element.on('ready', () => {
setIsStripLoading(false)
})
}
}, [elements])
return (
<form id='payment-form' onSubmit={handleSubmit}>
{isStripeLoading && <MyLoaderComponent />}
<PaymentElement id='payment-element'/>
<button id='submit' disabled={!stripe || !elements}>Pay</button>
</form>
)
I am currently working on a fun project and I want to know how to do something that has stumped for a bit.
Basically I am using Axios to get my data and then rendering data out in a .map func then I have a click function to show only the data that is corresponding to the ID for example ID 1 has some values that I want to show in another component. How do I do that?
https://j99t7.csb.app/
If you see my sand box and click on one of the ids and see the console / code - this is where I am stuck at.
Cheers,
Dave :)
In order to filter the data, you can use something like:
const [filteredData, setFilteredData] = useState([]);
//onclick
setFilteredData(data.filter(element => element.id === id));
//jsx return
filteredData.map(filteredElement => {
//loop through elements and display data desired
})
Though I'm not a React Master, onClick doesn't return JSX or TSX.
i.e where would the returned value be rendered, in most cases, it used as a void function with no return value
I use a react module that manages the use of spreadsheets:
react-spreadsheet https://github.com/iddan/react-spreadsheet/
In my project, I need several spreadsheets, and therefore several tables that the user can add, modify, delete on the fly
At the beginning, I wanted to use a big useState variable where the data arrays would be concentrated in the form : stateData = [ [...arrays], [...arrays...] ]
But the module, which uses an OnChange function taking as value the setData, seems to bug when my stateData variable contains different arrays.
<Spreadsheet key={index} className='table' columnLabels={stringLabelCol} rowLabels={stringLabelRow} data={stateData[index]} onChange={setData} />
As I can't manage everything in one UseState with this module,
is it possible to create a series of UseState inside a loop ?
It seems to me that it is a bad practice but I am really blocked
Thank you for reading my text... :/
Seeing your code and your comment, I'm assuming that:
You have one useState named data with ALL your data in it
You have multiple spreadsheets
For each spreadsheet, you want to pass the sheetData and the onChange that updates it
The issue in your code is that you're setting onChange={setData}, meaning any change will override the WHOLE data object, not just the targeted index.
What you want to do is have your onChange function be a function that updates only the right part of your state. So you'd do it like so:
const [data, setData] = useState([])
const updateSheetData = (index, sheetData) => {
// We clone the current state
const tempData = [...data]
// We update the right index
tempData[index] = sheetData
// This is now the new state
setData(tempData)
}
//...
// the onChange function for our component will use our custom function
<Spreadsheet
key={index}
className='table'
columnLabels={stringLabelCol}
rowLabels={stringLabelRow}
data={stateData[index]}
onChange={(newData) => updateSheetData(index, newData)}
/>
Then you'll also have to correctly update the data state whenever you add a new sheet or remove a sheet, to make sure the index are updated. But the idea is the same
I have a simple input field like this:
<TextField
required
variant="standard"
type="string"
margin="normal"
fullWidth = {false}
size="small"
id="orgName"
placeholder="Organisation Name"
label="Name"
name="orgName"
// defaultValue={orgData ? orgData.orgName : ""}
//inputRef={(input) => this.orgName = input}
value={this.state.orgName || ""}
onChange={this.handleChange("orgName")}
error={this.state.errors["orgName"]}
/>
I want to use the same input field for new and update? For new I just set the state to empty, and save the values. Which works fine. Now I have a select dropdown to edit the previously saved objects.
My problem is with editing, and I am tearing my head out trying to find the any way to do this. All these are the corresponding issues:
If i set the state from props - any edited changes are being reset
If i don't set the state from props, I get all blank fields, which is incorrect.
If I use defaultValue to load the form inputs from props, then its only called once. And it does not reload when I change the object to be edited.
If i just use onChange handler for, the form gets creepy slow, with many inputs on page
If I use refs, I am not able to reset the refs to reload the input when the object to be edit changes.
I have managed to make it work with componentWillReceiveProps, but it is deprecated, and react website is saying its dangerous to use it.
componentWillReceiveProps(nextProps) {
if (nextProps.orgData !== this.props.orgData) {
this.setState({
"orgName": nextProps.orgData.orgName,
"orgPath": nextProps.orgData.orgPath,
"orgAddress": nextProps.orgData.orgAddress,
"orgPhone": nextProps.orgData.orgPhone,
"orgEmail": nextProps.orgData.orgEmail,
})
}
}
So how can I actually create an editable form where the values have to be loaded from props for different instances from db, but they have to controlled by the state. There has to someplace where I have to check saying "hey if the props have changed, reset the state with the new props for edit???
This has been the most frustrating experience using react for me. How are there no examples anywhere on how to build a simple form to create new, and editable object using react and redux. It just seems overly complicated to do such a simple thing, the whole thing just sucks!
There has to someplace where I have to check saying "hey if the props have changed, reset the state with the new props for edit???
Yes you can use React.useEffect hook on the special prop or Array of props, then when that/those prop(s) change the internal hook function will be fire.
e.g. :
const MyComponent = (props) => {
let [myState,setMyState]= React.useState(null);
React.useEffect(()=>{
setMyState(props.prop1);
},[props.prop1]);
return ...
}
I am trying to implement a table in react where the user can edit individual rows by clicking the edit button on a row and then submit once he has made his change. I have say two components App.js and its child Table.js to implement this.
The way I thought of doing this initially was letting each of this component have their own state for rows and then the Table component reads from the props send to it by parent initially and only change the parent rows when users submits the change as oppose to onChange event. But I've read that reading props into state is an anti-pattern.
So decided to have everything in the parent by having two values for row (oldrows,newrows). And using them to maintain state instead, This is the design I came up with :
But what happens is whenever I click cancel the oldRows get bound to the newRows, here is a codePen example I put up:
https://codepen.io/snedden-gonsalves/pen/zYOVMWz
handleChangeRowInput = (event, keyValue) => {
let keyVals = [...this.state.newValuesArray];
keyVals[this.state.editIndex][keyValue] = event.currentTarget.value;
this.setState({
newValuesArray: keyVals
})
}
handleCancelRowInput = () => {
this.setState({
newValuesArray: [...this.state.oldValuesArray],
editIndex: -1
})
console.log('array', this.state.newValuesArray)
}
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: [...this.state.newValuesArray],
editIndex: -1
})
}
In the codePen example if you enter a new value then cancel and then try adding a new value again the the old values and new values get bound.
I tried using lodash deepClone but it didn't work out, not sure why this is happening.
Also if you could comment on what is the best way to design this in react that would be awesome as I am very new to react and just trying to learn ..
I didn't find any issue after the cancel function. For me, the issue was coming up after I called the save function.
After clicking on the save button and then editing again, the old values and new values were get bound.
The handleSubmitRowInput function should create a new array for the oldValuesArray using the cloneDeep function
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: _.cloneDeep(this.state.newValuesArray),
editIndex: -1
})
}