Vue 3 Realoading component if variable change - javascript

I am trying to realod component if selectedLeague has been changed
<label class="label">Select league</label>
<div class="selector">
<v-select
v-model="selectedLeague"
:dropdown-should-open="dropdownShouldOpen"
:options="leagues"
label="displayLeague"
placeholder="Select a league"
/>
</div>
<div v-if="selectedLeague">
<LeagueTopScorers :is="selectedLeague" :selectedLeague="selectedLeague.id.toString()" />
</div>
In LeagueTopScorers component I am fetching API to get top scorers from selected league.
I tried :watch, v-on:, created()

<LeagueTopScorers /> does re-render when selectedLeague changes 1, but it doesn't re-mount.
It only mounts when selectedLeague changes from a falsy value to a truthy one (because that's when the value of the v-if changes).
There are a few problems with your question:
you're asking about a technical aspect (let's call it X), which you think will solve a business requirement (let's call it Y). It's a classical XY problem. Why don't you describe the Y requirement? (e.g: what you want to achieve). What output change do you want to see on what input change?
the code you posted is not enough to create a runnable example. We don't know what v-select is, what <LeagueTopScorers /> is, or how does :is prop look like on <LeagueTopScorers />.
From the snippet you posted, I am guessing the following:
v-select is either vue-select or Vuetify select component
you expect :is to work on <LeagueTopScorers /> as it does on <component />. Hint: it doesn't, unless you coded it yourself, which I doubt
last, but not least, I guess you want some code you placed into an init lifecycle hook of <LeagueTopScorers /> (e.g: onMounted) to run when you replace an object stored in selectedLeague with another object.
If I'm correct, the simplest and cleanest way to achieve this behavior is to create a computed 2:
const leagueId = computed(() => selectedLeague?.id.toString() || '')
... and use it in v-if, :key and :selectedLeague:
<LeagueTopScorers
v-if="leagueId"
:selectedLeague="leagueId"
:key="leagueId" />
(without the <div v-if> wrapper).
The above will create a new instance of <LeagueTopScorers /> every time leagueId changes and will only render one when leagueId is not falsy. I believe that's what you were trying to accomplish, technically 3.
Notes:
1 - to verify, use onUpdated(() => console.log('updated...'))
2 - use selectedLeague.value?.id.toString() if selectedLeague is a ref
3 - at the same time, I am sure the actual business requirement can be met without creating a new instance of <LeagueTopScorers /> every time leagueId changes, but I can't help more without more details and/or context

Related

Storage-design for fetched data (globally?) & navigation

im new to JavaScript & React (and new to stackoverflow too) and I hope to get some ideas/ answers regarding the "logic" in implementing data-storage. I have/ had a dev-background in Obj-C / iOS few years back. So some general concepts in web-dev might not be familiar to me.
Hope I'm doing everything correct in my first post, never really used stackoverflow so far :)
So, basically I have a small project with a Sider und Content (using Ant Design Framework). In this project, im trying to figure out, how generell stuff has to be done, like Navigation through the views, data handling and so on.
In one Tab in the SideBar, i'm using a Table (also antd). If you press on one Item, it should show a detail-View with the props of that specific item (later it should be the form for data change/input).
My App.js:
<Content style=...>
<Switch>
<Route exact path="/tasks" >
<TasksTable
data={this.state.tasksData}
handleOnRowClicked = {this.handleOnRowClicked}
/>
</Route>
<Route exact path="/details">
<TasksDetail currentTask= {this.state.currentDetailTask}/>
</Route>
....
</Switch>
</Content>
In the TasksTable-Component:
...
const history = useHistory()
...
function handleOnRowClicked(record, rowIndex,event) {
console.log("Clicked on cell", event)
history.push("/details/")
props.handleOnRowClicked(record.id)
}
return(
<Table
dataSource={props.data}
onRow={ (record, rowIndex) => {
return {
onClick: event => {
handleOnRowClicked(record, rowIndex,event) }
}
}
}
>
<Column title="Name" dataIndex="name" key="name" />
<Column title="Kunde" dataIndex="client_name" key="client_name" />
<Column title="Fertigstellung bis" dataIndex="completion_date" key="completion_date" />
<Column title="HinzugefĆ¼gt" dataIndex="created" key="created" />
<Column title=""
dataIndex="remove_button"
key="remove_button"
render={removeButton => {
return <button key="ButtonKey" onClick={event => handleOnButtonClicked(event)}>Remove</button>
}}/>
</Table>
)
}
What i want to achive:
When clicked on one row of the table, i want to push another "view"/ Side to show details of selected row. Currently I'm doing this in the TasksTable-Component with history.push(). I'm wondering if this is the correct way of showing (detail-)views? Because I need to call props.myHandleCallbackFunc(selectedItem) in App.js to set a State names "currentlySelected" for then passing this state as prop to the pushed detail view.
Hope you get my point.
As far as I understood, I should use ... for automating the navigation-process. Problem is: As I use the Table from antdesign, I need to make this programatically. Thats why I have this workaround (at least it feels like one) with the onRow property... Is this the correct way of using the table? I have the feeling its not.
Problem with my workaround: I've rendered a Button into the Cells to deliver a delete-Button.
Problem there: Even if i click on the Button, it is registered as a click on the row and therefor pushes my detailview... I tried to differentiate with event.target.key but had no success.
Is it the right way of holding all data fetched by my api in the uppermost component and share it to sup-components via props? How can I update it from subcomponents then? Should every Subcomponent should get a handler via props to call back, when value changed?
Or in other words: Make the http-request from the sub-comp or send the changed data to the App-component and let it then trigger the HTTP-POST-request? I strongly guess its second choice.
Thank you so much, I appreciate every answer. Im trying to get things working since few hours now...
(Sorry for spelling mistakes, english is not my native one).
P.S: Actually I wanted to post this yesterday but took few extra hours for working through the "similar questions"... Still, open questions :D
Robin
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.
https://redux.js.org/introduction/getting-started

Material UI Autocomplete component not showing values from react state

I am trying to get value from the state for materialUI's autocomplete component.
I am facing the following problem : -
Autocomplte working fine for selecting the value and with onChange function it saving it into the state too.
But when I refresh my page/ re-render it is not showing value on the textfeild(from saved state):
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source}
getOptionLabel={option => option.text}
inputValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
I tried with the debugger and found its getting value in this.state.tidelocation_searchtext
but failed to set it with params.
Thanks in advance !!
Ps: I tried with defaultValue and search text nothing worked for me
following is my ONchangeFunction
handleUpdateTideLocationField = (str, value) => {
debugger
this.setState({tidelocation_searchtext: value.text});
}
after selecting a value,following value saved in sate :
tidelocation_searchtext: "Auckland"
So I found the solution all by myself by doing research and several hit and try, following is the solution of my problem:
<Autocomplete
name={"TideLocation"}
disabled={p.disabled}
options={data_source.map(option=>option.text)}
defaultValue={this.state.tidelocation_searchtext}
onChange={_this.handleUpdateTideLocationField}
onNewRequest={_this.handleChangeTideLocation}
onBlur={_this.handleBlurTideLocationField}
onUpdateInput={_this.handleUpdateTideLocationField}
renderInput={(params) => (
<TextField className="autoCompleteTxt"{...params} label="Location" />
)}
/>
Basically I was doing the following things wrong :
1.There is no need of using input inputValue={this.state.tidelocation_searchtext}& getOptionLabel={option => option.text}
2.as my data is in object form I have to convert it into a string so default value can match this from the state value
options={data_source.map(option=>option.text)}
Thank you all for your valuable support and solution !!
Removing inputValue has worked for me, even when passing object as options.
Using: "#material-ui/core": "^4.12.3"
If data/state not saved externally f.e. in local storage then it will be lost on page refresh, always. It's normal - like RAM memory without power - it (page/app state) only exists in memory !!
It's like using using cookie to keep you logged in.
If you really need such functionality then use f.e. redux-persist
You are right, if object type options are passed, material-ui's AutoComplete component does not seem to reflect the value when mounted. (Perhaps a bug?)
I was able to get around this by passing the proper characters to inputValue.
#RebelCoder
Maybe you should have initialized tidelocation_searchtext.
For me it was coming from the dropdown z-index which was hidden by another css behaviour.
I added this in a css file :
/* Dropdown MUI Component Autocomplete*/
div[role="presentation"].MuiAutocomplete-popper {
z-index: 1000000;
}
And it appeared finally. A bit hacky, but I think it was caused by another library that had something of that kind.
Note that I added several css elements to the selector, because just using the class wasn't enough.

Render props method without nesting dom element

In essence I have the following inside a react functional component:
<div className="content">
<div className="filters">
<MyFilter1 data={data}>
{(filter1Data) => {
return <MyFilter2 data={filter1Data}>
{(finalData) => {
return <div className="final-data">{finalData}</div>
}}
</MyFilter2>
}}
</MyFilter1>
</div>
<div className="filtered" />
I am using multiple filters with the render children paradigm to get the final data. But for (admittedly frustrating) styling reasons, I would like to render the final data in a div outside of the filters location (inside the 'filtered' div in this case). How can I do this? Is my approach wrong? Is the 'filter with render children' the wrong approach?
I have tried:
React portal with createRef(), in essence the problem ends up being that the ref is not available yet when the 'finalData' function is called.
Edit:
The filter components themselves are very complicated but I can give a couple examples. The data I'm filtering is a simple object with 12ish properties. One of the filters is implemented as a series of checkboxes determining which properties of the data I want to display (necessary as a separate component because of the number of fields). One of the properties on the data is a price, and another filter is a price range 'dialer', that specifies the price range to display ( aka exclude data points outside the range).

"toggled" attribute not working (redux-form-material-ui)

I'm trying to use Toggle from redux-form-material-ui:
import { Toggle } from 'redux-form-material-ui'
It works ok updating the toggled value to the store on its onChange:
<Col xs='3'>
<h3 className="title-page">Parceiro</h3>
<Field name="possui-parceiro" component={Toggle} label="Possui parceiro?" />
</Col>
Problem is: I make a call to some API and I need to update the value of this toggled "programatically". Theoretically, I can use the toggled attribute as stated here, but this just doesn't work:
<Col xs='3'>
<h3 className="title-page">Parceiro</h3>
<Field name="possui-parceiro" component={Toggle} toggled={this.state.someBloodyState} label="Possui parceiro?" />
</Col>
Which leads me to believe that in this case redux-form is just on the way of the update / manipulation process, forcing me to somehow update the form on the store to toggle the value, and it looks messy to dispatch such action. Anyway, how do you proceed in such cases?
I would store the API result in the redux state, read it from your mapStateToProps and pass it to the component in the property initialValues setting enableReinitialize: true.
These 2 properties are the way of redux-form to change stuff programmatically "later on" after the form already rendered.
Otherwise , if you can fetch your data before even rendering the form, you can just use initialValues without enableReinitialize.
Another way is to use the change function provided by redux form
More info in the docs

Which event to use for emitting change from inputs?

I have a React component that has many <TextInput> components, which are just simple wrappers around text <input> elements. Example:
<div>
<TextInput value={person.FirstName} ... />
<TextInput value={person.LastName} ... />
</div>
I also have some state represented by a person object. For example:
{
"FirstName": "Buster",
"LastName": "Posey"
}
Each <TextInput> component manages it's own internal state, which is the value of the text <input> at a given time.
When I click on a Save button, I want to make sure the person object has the most recent values from all of the <TextInput> components.
I can think of two ways to do this:
Events (onKeyDown, onChange, onBlur). An event inside the <TextInput> will create an action, it goes through the dispatcher, and the store updates the person object one field at a time.
Refs. I know this is an anti-pattern, but it really does seem so much simpler. I can iterate over refs when Save is clicked and pull out the state and fire an action.
What is the idiomatic way of doing this?
If it's just a simple wrapper around input, give it the same api as input, and don't have it manage internal state it doesn't need to.
render(){
var person = this.state.person;
return <div>
<TextInput value={person.FirstName} onChange={this.handleFirstNameChange} />
...
</div>
},
handleFirstNameChange(name){
this.setState({person: update(this.state.person, {
FirstName: {$set: name}
}});
}
update is React.addons.update, just as an example
And when save is clicked you send it out in an action. You can have a store for each piece of temporary data if you really need to, but it's almost always overkill if you don't persist it.

Categories

Resources