SweetAlert2: How to get back values from react component? - javascript

With sweetalert2 now there is sweetalert2-react-content, that lets you use React components to show inside Swal.
It's good, but I can't seem to find how to get back values from that component.
For example, I have a component, that has two inputs.
MySwal.fire({
html: <MyComponent />
}).then(value => {
// how to get here my values from MyComponent??
});
I want get whether checkboxes are checked or not from that component.
I tried to have the state for those inputs and onChangeHandler in the component where I call MySwal. But that didn't seem to work at all, the checkboxes would not change.
In the previous version of this library there was swal.setActionValue, that seemed be what I need, but it doesn't work with the current sweetlalert2 version.
To sum up, when I press OK on the prompt, I want to get the value in the promise, that would be set by the MyComponent.
DEMO

You can try using preConfirm() as per the documentation. From perConfirm we can pass custom values.
Please find this example : https://codesandbox.io/s/kk2nv9nmy7

The following trick should do the job:
let returnedValue;
MySwal.fire({
html: <Options onChange={value => (returnedValue = value)} />
}).then(value => {
console.log(returnedValue);
});
Long story short you could pass onChange callback to the component and use it for updating a variable which stores a value. It's not very pretty but it does the job.
You could find the full example here https://codesandbox.io/s/o1005xv1q

Related

useState element not rerendering when triggered by an event handler in a child component

I have a parent component with a useState that is being displayed. What I want to do is have a child component be able to update this state to change what the parent is displaying. I currently have the following:
function Parent() {
const [myWindow, setMyWindow] = useState(null)
useEffect(() => {
setMyWindow(<Child updateWindowFunc={() => setMyWindow(someNewWindow)} />)
}, []}
return (
<div>
{myWindow}
</div>
)
}
function Child({updateWindowFunc}) {
//Somehow calls updateWindowFunc
}
(Note that my actual code is set up somewhat differently, so don't mind the syntax as much as the general concept.)
I've found that I can get the value of myWindow to change, but the actual display doesn't show any change. I've tried forcing a re-render after the change, and adding a useRef to display useRef.current, but nothing seems to update which window is actually being rendered.
What am I doing wrong?
Edit:
I've found that it works if I switch to a different type of component, but if its just a different element of the same component then there is no re-render. I've been using React.createElement(), so I would think the 'objects' are distinct, but maybe I just misunderstand how this works.
Can you provide more details or working\not working code example that has more info?
Currently it's hard to get, cause that doesn't make any sense since you can just render it like below and that code will have same result as one that you are trying to run
function Parent() {
return (
<div>
<Child />
</div>
)
}
So for better understanding and fixing a problem \ finding a different approach, please, provide more details

Making v-model:value lazy

I have a simple Vue component, which comes from a 3rd party package.
The component is a text editor, and I have to provide it with a value for it to render correctly.
<SomeComponent v-model:value="text" />
<script setup>
const props = {
records: {
type: Object,
},
}
const text = computed(() => props.records.first())
</script>
Now, I want to update my database everytime the text property is changed.
watch(text, () => {
//Post to database...
})
I don't, however, want to update the database on every keystroke - hence, I want to make the v-model lazy. This I am trying to do like this:
<SomeComponent v-model:value.lazy="text" />
However, this doesn't work. The code inside my watch method is being fired on every keystroke.
As shown in the documentation, v-model is just some sugar syntax.
So that those are the same
<input v-model="text">
<input :value="text" #input="event => text = event.target.value">
If you want to have your component updated on the change event, you could then use something like this
<SomeComponent :value="text" #change="event => text = event.target.value" />
Or this one rather
<SomeComponent v-model.lazy="text" /> <!-- text is the getter and the setter here -->
As shown in the documentation: https://vuejs.org/guide/essentials/forms.html#lazy
If you want something even more advanced, you could look into debouncing your inputs (more work but it's kinda more clever into handling when to update the state).
You do not need to use v-model:value="text", just v-model="text" is enough.
If you wanted the input to be lazy, without any extra functionality (like posting to DB on every change) then v-model.lazy="text" would be enough.
When using watch, it doesn't matter whether your input is updated lazily or instantly. The watcher watches for each and every change, i.e: every keystroke in the case of an input.
So if you wish to make it lazy and make a call to DB on every change, then you need to this:
<SomeComponent :value="text" #change="onChange">\
<script setup>
const onChange = (event) => {
text.value = event.target.value;
// post to DB
}
</script>
If this doesn't work, then the suspect is the 3rd party package. In which case I suggest you look at the package's documentation and see if they provide a prop for the text editor to be lazy.
The 3rd party plugin did not allow me to omit the v-model:value property, so I ended up using lodash' debounce method instead:
<SomeComponent v-model:value.lazy="text" />
watch(text, _.debounce(function () {
//Post to database...
}, 500));

Need some suggestions for a dynamic form using React

I'm building an enterprise-level application and I need some tips and suggestions for handling dynamic form.
The fields of the form are totally dynamic and they come differently for each user.
I loop through each field(fields come from an API call) on a file called renderUiType.js and based on a property of the field called uitype, we render different Inputs.
For example if uitype===1{render TextField}, if uitype===2{ render Checkbox } and so on...
So far the displaying part is correct but now I want to save the values of each field rendered and have them all in an object so I can do a POST API Call
So my question is, how can I do that? Should I create an onChange handler function for each form-element at the main file renderUiType.js and then pass it with props to the form-elements components or should I use Redux?
Any suggestion/article or anything is welcomed. Thank you
The folder structure looks like the image below(just in case it helps to understand what I ask)
..
You can use one callback function and use it in each onChange component specific handlers. You could have everything in state of the Form if you would like hidden under the unique keys/id, so you don't need to have Redux. f.e.
if (uitype===1)
{render <TextField value={this.state[fieldId]} onChange={this.onChange}/>}
if (uitype===2)
{ render <Checkbox value={this.state[fieldId]} onChange={this.onChange}/>}
or to simplify:
const getComponentByUIType = (uiType) => {
switch(uiType) {
case 1: return TextField
case 2: return Checkbox
}
}
// ...
onChange = fieldId => value => this.setState(state => ({fieldId: value}))
//...
render() {
getComponentByUIType(uiType).map(Component => <Component value={this.state[fieldId]} onChange = {this.onChange(fieldId)} />
}
Using Redux for this shouldn't be necessary unless you need to access this form's state somewhere outside the form. If you only need the form info to do a POST, I would keep all the data inside one component's state.
Just use the unique ID provided by the IP (the one you were gonna use for the POST) to build that state object. Every field will have an onChange that updates the main form component's state, and then that same value from the state is passed in to each field as a prop.

ReactJS - ReactiveSearch - Custom event on suggestion selection

I am building an autocomplete component to search current users and add them to a team. The search works fine except I need to customize a couple things that I can't figure out how to do.
First, this component is modeled after GitHub's invite user modal. You can start typing to search for a current user and it populates any results it finds. However, if it doesn't find any results it only shows one item to invite that user via email. How can I edit the full state of the result list for a DataSearch component? All I can see is to edit the content of each suggestion via the onSuggestion prop. I need to be able to say, "If there are zero results, display this."
Second, when a suggestion from the autocomplete result list is selected, I need to be able to reset the search box because I am populating a list that the user can see. Right now, the search box is populated with the value of the suggestion. So, I am populating the list below of selected results just fine; but, I still need to be able to reset the search box when that result is selected.
Help????
CodeSandbox link
For the first part of the problem, you can use the prop onNoResults on any of the results components to render custom JSX when no results are found. From the docs:
onNoResults String or JSX [optional]
show custom message or component when no results founds.
<ResultList
...
// custom JSX when no results are found
onNoResults={
<section>
<input />
<button>Add</button>
</section>
}
/>
For the second part of the problem, there are two ways IMO you may approach this.
Using a ReactiveComponent which lets you create a custom ReactiveSearch component.
Using customQuery
I'll explain how to approach this using customQuery but it might be better to create a custom component depending on which approach suits your needs best.
In the example I've shared my DataSearch looks like this:
<DataSearch
title="DataSearch"
dataField={["original_title", "original_title.search"]}
categoryField="authors.raw"
componentId="BookSensor"
customQuery={this.customQuery}
defaultSelected={this.state.value}
onValueSelected={this.updateState}
/>
The reason for using a customQuery is to get full control of which query gets applied to retrieve the results. ReactiveSearch is designed to work reactively. When a value is set into the DataSearch, the ResultList would react to this change. Having a customQuery lets us control which query is fired for this change. Also I'm keeping the value of DataSearch in the state so I can clear it up when the query gets fired. Here's what I'm using in the example:
customQuery = (value, props) => {
// find the current query using defaultQuery from DataSearch
const currentQuery = DataSearch.defaultQuery(value, props);
// clear the value in component's state
this.setState({
value: ""
});
if (value.length) {
// store this query
this.prevQuery = currentQuery;
return currentQuery;
}
// if the value is empty, that is it was cleared, return the previous query instead
return this.prevQuery;
};
So whenever the value is cleared, I just return the previous query so the result list shows the correct results for the previous query (before the value was cleared).
Also in order to control the value of DataSearch from my component I'm using the defaultSelected and onValueSelected props. They work quite similar to how you would create a controlled component in react. Docs
Again, it might be better to create a custom component using ReactiveComponent if this approach sounds complicated to customize this flow.
Demo for this answer using customQuery

How to update a field value after action dispatched with redux

I would like to populate a form after an ajax call with redux.
My case is pretty simple:
I have a simple user form (with only one text field for now), it's a react view bound to the state with connect().
I call a rest API to fetch the user.
When the api call is done, an action is dispatched with the user.
A reducer update the store state with the user.
I would like to populate/update the form with the retrieved values.
Solution 1:
If I set the value from the props like that:
const Field = ({ field, onFieldChange, value }) => (
<input
value={value}
onChange={(event) => { onFieldChange(field, event.target.value) }}
type="text"
/>
)
It works but I get this warning:
Field is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
I understand why I get this error as I should not use a component to display something and also be able to update it.
I also tried to use the defaultValue props but this is only used at the component creation (and we don't have the user yet). After the ajax call return, defaultValue cannot be called.
Solution 2:
Use redux-form with a custom plugin to update the form model each time the state get updated. I don't find this solution really clean but maybe I'm wrong.
I really thin that I'm going in the wrong direction and that it should exist a better way.
Does somebody already faced this kind of issue?
I encountered the same problem when I was trying to pass undefined as the input value.
To fix this, ensure that you are passing at least empty string to the input, not undefined
const Field = ({ field, onFieldChange, value }) => (
<input
value={value || ''} // <- add fallback value here
onChange={(event) => { onFieldChange(field, event.target.value) }}
type="text"
/>
)
Actually you might try to make your component statefull - store and manage value of input inside of it (they say it's ok).
Or if you really need this value in the store use redux-form, I have realy good experience of using it (you'll have to write less boilerplate code).
By the way, you will not have to use any custom plugin, you can use initialValues, see more here
The solution above will work for sure, but it doesn't seem to be nice.

Categories

Resources