changing props in react_component when using react_rails - javascript

I am using this helper to display a datepicker component
<%= react_component "MyDatePicker", startDate: '', endDate: ''%>
I want to pass javascript values to startDate and endDate props. Any possible way to do this?

I don't really know what you're trying to do here exactly but if you just want to get values of your props from the component to your rails controller, do the following.
You can set state of your props in your react component and send an ajax request whenever the user selects a date.

Fixed this by using Pubsub. What I did is publish the user selected values in the javascript first. Then on react parent component lifecycle method subscribed to the previously published event and use setState to change the state based on that.
Now am on mobile but i will publish the code for clarity once i got access to a pc.
update
Using pubsub is easy. First we need to publish the required from anywhere using javascript
dates={some_value:value, another_value: value }
PubSub.publish(OFFLINE_BOOKING_DURATION, dates)
Here I just pass a dates object. You can pass anything.
Then in react's componentDidMount state I subscribe to this
componentDidMount: function () {
this.token = PubSub.subscribe(OFFLINE_BOOKING_DURATION, this.subscriber)
},
Here the first is object we are expecting and the callback
so here is the call back function here you can do anything like ajax call, set state, or anything
subscriber: function (msg, data) {
#this method gets called on receiving data
#we can access the data we passed like this
data.some_value},
And finally unsubscribe when component unmounts
componentWillUnmount: function () {
PubSub.unsubscribe(this.token)
}
This is how I implemented it. I no longer have the code but I hope you got the point.

Related

Fetch graphql query result on render and re-renders BUT LAZILY

I have a React Apollo app and what I am trying to do is that I have a component that renders some data using charts. For this data, I have some filters that I save in the local state of the component (Using hooks)
const [filters, setFilters] = useState(defaultFilters);
Now what I want is that whenever the component mounts, fetch the data using the default filters. But I also want to re-fetch data when the user updates the filters AND CLICKS ON SUBMIT and I'd fetch the results using new filters.
Since I also want to fetch the results on filter update, I am using useLazyQuery hook provided by apollo
const [getData, {data}] = useLazyQuery(GET_DATA_QUERY, { variables: {filters} });
useEffect(getData, []); // this useEffect runs only when the component mounts and never again
But, what happens is whenever my state, filters, updates the getData function is automatically run! ALWAYS! (BEHIND THE SCENE)
How do I handle such cases, where I want to fetch results on mounting and re-rendering.
I have tried using useQuery and refetch provided by it but I get the same problem there, whenever I update the state, the component rerenders and the useQuery hooks is run and makes the call. (That's how I believe it runs)
How do I fix my current code. Calling the getData function inside the useEffect function makes it run on every re-render.
I think I the problem defined in this stackoverflow-question is somewhat similar to mine.
Part of the problem is that you really have two different states that you're trying to utilize a single hook for. You have state that represents your inputs' values in the UI, and then you have state that represents the filters you want to actually apply to your charts. These are two separate bits of state.
The simplest solution is to just do something like this:
const [inputFilters, setInputFilters] = useState(defaultFilters)
const [appliedFilters, setAppliedFilters] = useState(inputFilters)
const { data } = useQuery(GET_DATA_QUERY, { variables: { filters: appliedFilters } })
const handleSubmit = () => setAppliedFilters(inputFilters)
const handleSomeInputChange = event => setInputFilters(...)
This way, you use inputFilters/setInputFilters only to manage your inputs' state. When the user clicks your submit button, the appliedFilters are set to whatever the inputFilters are at the time, and your query will update to reflect the new variables.

React: Is there a way to capture changes to props sent via history.push

I'm writing a component that uploads parameters to the server in promises. While the parameters are uploading I redirect to another component and send the parameters object as props, like this:
this.props.history.push('/new-page/createForm', {
parameters: this.state.parameters
});
At the create Form component, I'm receiving the props and rendering the state of the upload of the parameters, and they render as uploading (because they haven't finished uploading).
Back to the previous component, once the parameters upload promise finishes, I update the parameters object to reflect the new state, i.e, uploaded.
ParamsService.uploadParam(parameter, uploadRoot)
.then(() => {
parameter.status = 'Uploaded';
})
Now, once the parameters are uploaded, I want the create form to automatically re-render in order to reflect the new state of the parameters, but this is not happening automatically.
However, if I perform an action in the form, such as filling one of the input fields, which trigger a React rendering, the parameters show as uploaded.
Is there a way to recognize the props change in the new create form and re-render once the parameters state change to uploaded?
I've tried componentDidUpdate and getDerivedStateFromProps but such functions do not trigger when the parameters status change to uploaded.
Any help or guidance is appreciated.
see if react-url-query solve your problems.
as the example in readme, once you use the HOC like
export default addUrlProps({
urlPropsQueryConfig: {
foo: { type: UrlQueryParamTypes.number, queryParam: 'fooInUrl' }
},
})(MyComponent);
MyComponent will receive 2 props this.props.foo and this.props.onChangeFoo which allow you to get the value and allow you to change the url params ?fooInUrl
Solution for React hooks: use-query-params

Best practice with react redux refreshing data with updated date range

I'm using react-redux for my new project and I'm looking for advise on best practice to do the following.
I have a container with 2 components.
Component A which I'll use to update date range
Component B which is going to display data with updated date range from 1st component.
So, in component A,
I'm using axios to call my API to update dates
On success, I'm using axios again (inside the success promise) to call API to re-get data (with new date range) and call reducer to update the state.
It feels wrong to call API nested in success promise. Is there a better way to do this?
Should I be calling refresh data somewhere else other than in component A? like action?
Sorry this is a bit of a noob question but trying to get my head around redux architecture.
There is no need to call the API again in component B . You have to use containers for component A and Component B and once you update the date in component in A save those values in redux store. and in component b container you will get that updated values from sate like
const mapStateToProps = (state) => {
return {
upadteData: state.upadteData,
}
}
and component B you just use
this.props.upadteData

React - Passing state to parent component - too many AXIOS requests

I'm a beginner both in programming and React and I have to create a functioning google Map single page website. I'm using google-map-react.
I have a parent App.js (containing the call to and a HTML sidebar) and a child Map.js containing the map itself and axios request function.
I'm making axios requests to fetch data from foursquare api. It works without side effects. Then I want to pass those data to my app.js and update the parent state so that I can renderthe locations on the sidebar.
This is the function I used (in Map.js). I had to put the call in componentWillReceiveProps as a last resource because componentDidMount didn't work:
https://jsfiddle.net/kd1yuhe5/
I think this may be the issue, but it's also the only way I found to make the list show:
this.props.updateVenues(this.state.venues)
This is the code from App.js
updateVenues(venues) {
this.setState({
venues: venues,
});
}
Then I called the method like this:
<Map updateVenues={this.updateVenues.bind(this)} />
The code works, venues are shown in the sidebar (if you need the code let me know, but I don't think it's relevant), but the I keep making requests until I exceed quota.
Again: I'm a beginner. I just started 3 months ago.
EDIT:
Here are both components:
Map.js
https://jsfiddle.net/kd1yuhe5/5/
App.js
https://jsfiddle.net/xwzrm4bp/2/
When the state of a React component is updated (and without custom implementation of componentShouldUpdate), it triggers a re render of that component (ie call the render function).
If the props of the children of this component have changed since the last render, they will also re render.
They re render because they have received new props, and this will also call their componentWillReceiveProps function.
Since you are fetching data each time Map will receive props, you are fetching data each time something change (state change) on App.
First in Map.js, this.props.query is assigned to this.state.query.
This looks like an error, as in this case what you want are the new props receceived by componentWillReceiveProps, this is the first argument of this function.
So you should assign props.query to this.state.query instead.
Except that actually you should not:
this.state.query is only used in componentWillReceiveProps, therefore there is no need to put props.query into state.query.
Second since you have both this.props.query from the previous props update and props.query which is the new received query, you have the opportunity to fetch only when the query has actually changed:
// Receive the update query from parent and fetch the data
componentWillReceiveProps(nextProps){
if (this.props.query !== nextProps.query) {
this.fetchData(nextProps.query);
}
}
Now you may ask, "ok but why my Map component was always re rendered, even when its props didn't changed".
But they did:
in App.js
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues.bind(this)}
getClickedMarker={this.getClickedMarker.bind(this)}
/>
By calling this.updateVenues.bind(this) and this.getClickedMarker.bind(this) in the render method, you are creating new values (actually new Function references)for the updateVenues and getClickedMarker props, at each render.
Instead, you should bind these method in the contructor of App:
constructor(props) {
super(props);
this.updateVenues = this.updateVenues.bind(this);
this.getClickedMarker = this.getClickedMarker.bind(this);
....
}
....
<Map
query={this.state.query}
center={this.state.center}
updateVenues={this.updateVenues}
getClickedMarker={this.getClickedMarker}
/>
This may limit your API calls a lot, you may also debounce them.

How to pass correctly( and beautifully) an onChange event to my Redux Store?

Im building a Betting aplication where the user inputs some predictions for Soccer Games.
My app have a array of matches in my Redux store. Like this:
matches:{{id:1, away_result:0, home_result:0, ...}}
So, I have a Matchlist Container that reads this data from my store and passes each one as props for a Match Component. My Match component has something like that:
<TextField value={this.props.game.home_result} onChange={this.homeScoreChangedHandler}/>
I want that in each onChange event of my TextField my data on the Redux Store to be updated.
For example: My bet is Brazil 1x0 Argentina. I want to, when I change the TextField and input "1", an action is dispatched and update my store:
matches:{{id:1, away_result:0, **home_result:1**, ...}}
But I dont know how to pass it correctly because at my homeScoreChangedHandler I dont have acess to the "this.props.game" property.
Does anyone have any idea on how to deal with this?
You can pass the value to your onchange method.
Something like:
<TextField value={this.props.game.home_result}
onChange={(event) => {this.homeScoreChangedHandler(event)}} />
//method to trigger action creator
homeScoreChangedHandler(event.target.value){
this.props.nameofActionCreator(event.target.value)
this.methodToUpdateTextField(event.target.value)
}
You would need to update the component with the new props. You can use the react lifecycle method 'componentWillReceiveProps' or by using a callback method and set the component state to from the redux store (via reference to this.props.storeObject that you would set up in your mapStateToProps section). Your biggest issue is going to be avoiding async issues where the store doesn't update fast enough for your data to update.
So I would do some conditional rendering/ use a Promise to control rendering.
In pseudo code:
if (this.state.userInput != this.props.game.home_result){
update the react state to the props and render the new data }
else { don't update and just return the current state}

Categories