Don't loadOptions on initial render - reactjs

I have a ton of selects on my page and I don't want to fire off API calls for all of them every time the page loads. How can I skip loadOptions when async react-select components initialize?

According to the docs, it looks like you can specify autoload={false}. From the docs:
Unless you specify the property autoload={false} the control will automatically load the default set of options (i.e. for input: '') when it is mounted.
Updated:
To get around the issue where you have to type something, you could add an onFocus callback. This will fire when the react-select component is clicked on or tabbed to, instead of waiting for the user to type something. It might look something like this:
<ReactSelect
...otherProps
onFocus = { this.fetchData }
/>
To prevent constantly re-fetching the data, you might want to keep track of whether or not the data has been fetched in state somewhere. You could declare the initial state as fetched: false, then in the function that loads the options, set this.setState({ fetched: true }). Finally, your fetchData function can check this.state.fetched before requesting new data.

state= {option1:'', option2:'', option3:''}
If option 1 is filled out render select #2's values
this.state.option1 && apicall for option 2().
If option 2 is filled out render select #3's values
this.state.option2 && apicall for option 3().
All of this can be in componentDidMount()
This is assuming when the user selects a value you are storing that value in the state.

Related

How to let Query are performed at the component onmount and triggered by user event later?

My page has a search form and table with a paginator. So I have two states: searchCondition and pagination. I need to query the channelList when the page component is mounted.
When the user clicks the paginator, the pagination state will be changed so that a new channelList query will be made. That's ok.
The search form includes two input form items and a "Query" button. The channelList query also can be triggered by the user click event.
But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.
I read the doc Dependent Queries, there is an enabled option, but don't think it can solve my issue.
Currently:
Send the query on the component mount. - done!
Made a new query when pagination state changed - done!
User types something in the input form item(this will cause the searchCondition state changed) and clicks the "Query" button, then made a new query - undone.
When sending the channelList query, I will make the API parameters by merging searchCondition and pagination states.
const { searchCondition, setSearchCondition } = useSearchCondition(initialSearchCondition);
const { pagination, setPagination } = usePagination();
const channelListQuery = useQuery(
['channelList', pagination, searchCondition],
async () => {
const getChannelListParams = omitFalsyFields({
currentPage: pagination.current,
pageSize: pagination.pageSize,
...searchCondition,
});
const { code, result } = await apis.channel.getChannelList(getChannelListParams);
if (code === '0') {
return result;
}
},
{ initialData: { totalItem: 0, resultList: [] }, refetchOnWindowFocus: false, keepPreviousData: true },
);
But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.
What you need for this to happen is to have two state. One inside the form so that users can type (this can also be an uncontrolled form), and another one that you set when the user clicks the "Query" button.
That second state is what your query should "listen to" - by being part of the query key. Pseudo code:
function App() {
const [search, setSearch] = useState()
const { data } = useQuery(['channelList', search], () => getChannelList(search), { enabled: !!search }
<SearchFrom onSubmit={setSearch} />
}
by making sure to only call setSearch when the user clicks the Query button, you make sure that the query doesn't fire on every click.
A good place to store "applied searches" is also the url: When the user clicks the button, you write to the url, and the query subscribes to that.
This also makes your urls sharable, which is nice.
I have a short example on that here:
https://codesandbox.io/s/react-query-url-filtering-h0ikw?file=/src/App.tsx
The filter form itself is uncontrolled - but it doesn't really matter how you solve it, it's "internal" to the filter form. The "global" client state lives in the url.

How can I make sure a React parent component loads data before its child component mounts?

I'm writing a simple calendar application that uses a common layout to wrap different views of events (month view shows a larger calendar with all the days of the month and events for each day, week view just shows a vertical list of events for that week, etc.). The common layout includes a calendar picker control for selecting the date, and then a list of event categories that can be checked or unchecked to show events relating to sports, entertainment, etc.
When the layout mounts, I'm calling an async Redux action creator to get the list of event categories from the database. When those are retrieved, they're saved in a Redux store with a property of Selected set to true, since they're all selected at initial load.
async componentWillMount() {
await this.props.getEventTypes();
}
When the month view, which is a child of the layout view, mounts, it's grabbing all the events for the given month. Part of the selection process of getting those events is sending the list of selected event categories to the backend so it only gets events from the selected categories.
async componentWillMount() {
await this.props.getWeeks();
}
The problem is, the selected categories list is always empty when the month view goes to grab the events for the month. So it's not going to select anything since no categories are selected.
It seems the only way this can be happening is if the child component is mounting first, or if the parent component is taking so long to get the event categories that the getWeeks process finishes first (this is unlikely as the process to grab the weeks and days and their events is much more involved than just selecting the event category list).
So, how can I make sure the parent component grabs the event categories from the database and puts them in the Redux store before the child component selects its events?
I know one way, probably the best way, to do this would be to have the list of event categories render into the page on the server side, so it's just already present at initial load. I'll probably end up doing it that way, but I'd also like to know how to do it all through client-side actions, in case I need to do it that way for some reason in the future.
You can try like this
Set isDataLoaded when data is available.
Use ternary operator for conditional rendering.
In you render
return(
<>
....
{ isDataLoaded ? <ChildComponent /> : null }
....other sutff
</>
);
Use can also use the && operator
return(
<>
....
{ isDataLoaded && <ChildComponent /> }
....other sutff
</>
);
You can integrate componentDidUpdate() and use it to render your child-components in a somewhat synchronous flow.
Let's say the structure of your Parent Component should look something like the following:
Parent
class Parent extends React.Component{
state = {
renderChildren: false
}
async componentDidMount() {
await this.props.getEventTypes();
}
componentDidUpdate(prevProps){
if(this.props.yourUpdatedReducer !== prevProps.yourUpdatedReducer){
this.setState({
renderChildren: true
})
}
}
render(){
const { renderChildren } = this.state
return(
{ renderChildren ? <Child/> : "Loading" }
)
}
}
You want a key in your state that determines whether you should
render the Child component.
In componentDidMount(), you call the action-creator function, when
it completes, you get updated props.
With updated props, you trigger componentDidUpdate(), where you
check the values in your reducer. If the values are
different that means you got the updated data from your database, so
everything has loaded.
Great, so now you want to mount your Child component, so you
update-state, setting renderChildren to true, thus re-rendering the
Parent component. Now Child gets rendered and should behave as expected.

Display a Spinner while loading Parent State Array

I am using React with typescript for my application. What I have now is in my top level “app.tsx” a state called objects which is an array of objects. I have passed down through a few child components a function that is getObjects which is defined in the top app level. This function returns the state object[] if it is defined otherwise calls an asynchronous function to load in the objects.
What I would like to do is to display a loading spinner in my child component while this state object[] is still undefined in the app.
I know that if I were to load the object[] as a local state in my child component I could easily accomplish this. Is there any way that I can call a callback at the child level after the state in the app has finished loading? The only way that I see to do it is to pass the object[] itself to the child component, but my knowledge is still very limited. Thank you, I appreciate all help.
Edit:
I do need the getObjects function in my grandchild component. I've simplified the structure a bit. What really is the situation is in the app object is a Dictionary type that acts as a key-value pair with a number id corresponding to a specific array of objects. On app loading, one key-value pair is chosen and that is loaded asynchronously while the user can keep using the app. It loads in the backend for these initial child components so a spinner is not needed.
Later in the greatgrandchild component, there is a need to, if chosen by the user, load another id into the dictionary while using an API to populate the object[] portion of the dictionary. Once this portion of the dictionary is loaded, the getObjects call in the top app layer returns a converted array of objects in my greatgrandchild to be used for selection. What is happening now is that the selectbox is just being empty until the async function completes, then it populates as expected. What I would like to do is to display a spinner in the greatgrandchild in place of the selectbox while this operation completes.
Since the structure is a dictionary and technically two values for the dictionary could be trying to be loaded at a time, I'm not sure that I want to try to pass through and use a boolean for this loading.
I am reluctant to pass through the whole dictionary itself and wait for the particular dictionary key-value pair to be loaded since this seems like an anti-pattern. What is the best way to accomplish what I am trying to do?
In your child component conditionally render the spinner and the object contents.
{
yourObjectFromParent
? (<>Your object contents here</>)
: (<LoaderComponent/>)
}
OR
You can try to set a state called loading=true in the parent and set it to false when object is loaded. You should pass this loading state as props to the child components. If thats the case your code will be like this. This can also be implemented with a global state handler like redux.
{
props.loading
? (<>Your object contents here</>)
: (<LoaderComponent/>)
}
This would show a spinner until loading is completed.
class MyComponent extends Component {
state = {
isLoading: true,
data: null
}
componentDidMount() {
fetchSomeData(url)
.then(data => {
this.setState({isLoading: false, data: data});
})
}
render() {
const {isLoading, data} = this.state;
return isLoading ? <ChildComponent data={data} /> : <Spinner />
}
}

UI state vs Redux state

So my application has 3 drop-downs, a pagination component and a table.
Two drop-downs receive their options from rest call.
eg: State and City.
Based on the state selection the city dropdown makes rest call to fetch city.
Based on the selection made in the drop-downs the data in the table updates by making rest call.
The react.org example talks about lifting the state up. Hence the parent component which displays all the three drop-downs and table maintains the state of keeping track of which state, city and date is selected and what is the data that is being displayed in the table. Every-time the dropdown selection changes the parent state updates and re-fetches the new data.
Also the the table data updates every few seconds by sending the selected
state, city and date.
The question that I have is should the rest call to fetch the information regarding the dropdown options be in parent component or respective drop down components so that I can reuse the drop down in other page and do I need the dropdown to have there own state to keep track what is the selected value?
Should the rest call to fetch the information regarding the dropdown
options ?
The rest call to fetch the data should be part of the parent component only.
It makes sense to keep the dropdown component as a pure component which receives the options values as props from it's parent.
Following this method would really make your dropdown component as reusable component who only job is display the options and pass back the selected value to it's parent via a callback function passed as prop to the dropdown component.
Do I need the dropdown to have there own state to keep track what is
the selected value?
No, you can pass the selected value back to the parent using a callback function.
Some Code
Parent Component -->
constructor(){
this.state = {
stateOptions: null,
cityOptions: null,
selectedState: null,
selectedCity: null
}
}
componentDidMount(){
fetch.state.api => {
this.setState({
stateOptions: stateDataFromApi
})
}
}
handleStateSelection = (selectedState) => {
fetch.city.api => {
this.setState({
selectedState: selectedState,
cityOptions: cityDataFromApi
})
}
}
handleCitySelection = (selectedCity) => {
this.setState({
selectedCity: selectedCity
})
}
<div className="parentComponent">
<Dropdown
type="state"
handleStateSelection="this.handleStateSelection"
data={this.state.stateOptions}
/>
<Dropdown
type="city"
handleStateSelection="this.handleCitySelection"
data={this.state.cityOptions}
/>
<Table data={this.tableData} />
..
..
</div>

Manually update DraftJs ContentState with clicked text

How can I manually update DraftJs's ContentState in response to clicked text?
I have a list of text item. When one is clicked I am passing that text down to Draftjs, but because I am setting the state using componentWillReceiveProps() it requires that I click the text twice to get an update.
componentWillReceiveProps() {
const activeNoteText = this.props.activeNoteText;
if (activeNoteText !== '') {
this.setState({ editorState: EditorState.createWithContent(ContentState.createFromText(activeNoteText)) });
}
}
First click: Update the App state and pass props down to Draftjs (component updates before receiving new props)
Second click: Now the prop is properly set and Draftjs updates (component updates with the props received on the first click)
How can I accomplish this in one pass? I know there's no componentDidReceiveProps and I know there's a good reason, though I can't claim to fully understand yet, so what's the best practices way to accomplish something like this?
Why are you using componentwillReceiveProps?.
What you can do, is have the states your setting in Draftjs i.e. editorState(Well, that's what I can make out) in its parent and whenever a list item is clicked on the click handler for that update the editorState and then pass it as props to Draft js.
Further for the condition, where you are checking if it is not empty,
You could use the
getInitialState(){
.....
}
For initialization when your component is initially loaded. So you could have a default value for editorState.

Resources