The whole GraphQL paradigm is new to me, usually working with React Redux. My requirements are:
User clicks a Row Item w/ UID
We open a form to edit this data (with previously saved information pre-populated)
We then edit this data and save
I would think (2) & (3) would be handled by a <Query><Mutation/><Query> type of structure but it doesn't work, mainly because setting state in the Query will make the textinputs controlled inputs... controlled by <Query>, and I can no longer edit it.
Besides this, I've read that GraphQL removes the need for Redux, etc? So I'm reluctant to go around sticking this query in a middleware and propogating to Redux.
Any thoughts? This must be a common requirement. What have others came up with?
Your data should be passed down as a prop to whatever component will actually render the form. Inside that component's constructor, you then set the initial state based on the props. A rough example:
class FormComponent extends React.Component {
constructor () {
this.state = this.props.initialState
}
render () {
// Render form using this.state and this.props.update
}
}
<Mutation mutation={SOME_MUTATION}>
{(mutate) => (
<Query query={SOME_QUERY}/>
{({ data, loading, error }) => {
if (loading) return <LoadingIndicator/>
if (error) return <ErrorComponent/>
if (data) return <FormComponent initialValues={data.someQuery} update={mutate}/>
}}
</Query>
)}
</Mutation>
The mutation could go inside the Query, or inside the FormComponent itself -- that bit doesn't really matter. We're not rendering the FormComponent until we have data, so the initial values will always reflect the results of the query.
I'm using React Context to communicate between components (actions)
Context.Consumer resides inside the return() part of a component and as such doesn't provide props to the component (unless Consumer is put in parent component but then we're back to the badness of props drilling)
Apollo client is applied by Apollo's compose() to that the result is available in component props but more importantly, a change in props leads to Apollo refetch
Using Context, this automatic refetch pathway is broken and I see no other way but to bring the whole GraphQl document into the component (after Context.Consume) to manually do a refetch when Context brings in action.
This is a radical change in pattern and I really wonder if I'm missing some big picture?
const Child = ({ gqlGetData }) => {
if (gqlGetData.loading) return "..."
const { componentData } = gqlGetData
return (
<AppControl.Consumer>
{appControl => (
<React.Fragment>
---component rendering logic here ---
---but needs also refetch logic and GraphQl document---
)}
</React.Fragment>
)}
</AppControl.Consumer>
)
}
All I want is to run a piece of javascript code to query back-end GraphQL server. Why do I have to wrap my query in a HOC component? Like it says in this document.
import { Query } from "react-apollo";
import gql from "graphql-tag";
const ExchangeRates = () => (
<Query
query={gql`
{
rates(currency: "USD") {
currency
rate
}
}
`}
>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return data.rates.map(({ currency, rate }) => (
<div key={currency}>
<p>{`${currency}: ${rate}`}</p>
</div>
));
}}
</Query>
);
It looks like a very clumsy awkward solution? Why it has to be this way? Does it make things easier or more difficult? What is the philosophy behind it?
Update:
One thing trouble a lot is: I just want to make an API call, which is not visible, why do I have to render a tag inside render() function? API call are not supposed to be visible at all. This twist make me feel this whole HOC thing is a hack, bogus. What do you think?
They use Render Prop Pattern because of it's highly declarative nature as outlined here
This encapsulation makes composing your Query components with your presentational components a breeze
Now about the Render Prop itself, as per official react docs
The term “render prop” refers to a simple technique for sharing code between React components using a prop whose value is a function.
A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
As described here this technique is React's favoured way of handling cross-cutting concerns.
Components are the primary unit of code reuse in React, but it’s not a always obvious how to share the state or behavior that one component encapsulates to other components that need that same state.
So with render prop you put out only the public outcome of your encapsulated state that you want to share with other components.
render prop is not HoC, but is an alternative to it, that was recently embraced by the react team itself.
I was wondering if anyone has a good example of an update form using Apollo Client and the new query and mutation components. By update I mean:
Populate form values with query results
Update the component state (or apollo-link-state?) when the input is edited.
On submission update the state (or apollo-link-state?)
I'm struggling with it right now and I'm wondering what the best way to build it would be. Should I be using Apollo-link-state to store the form state? If not, I can't find a way to map the props from the query component into component state object without using the query HOC. Any examples would be great!
Thanks!
If the state doesn't need to be accessed by other parts of your app, apollo-link-state is probably overkill -- regular component state will do just fine. Just create a component like you would when using the HOC:
class MyComponent extends React.Component {
constructor (props) {
super(props)
this.state = {
fieldA = props.myQuery.fieldA
fieldB = props.myQuery.fieldB
}
render () {
// your form fields here
}
}
}
You can then just do:
<Query>
{({data, loading})=>(
if (loading || error) return null
<MyComponent myQuery={data.myQuery}>
)}
</Query>
I'm still new to react/redux, after getting something like this to function
User.js
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
return (
<Profile />
)
}
export default connect(null, {fetchUser})(User);
Profile.js
class Profile extends React.Component {
render() {
const { user } = this.props
return (
<h1>{user.profile.name}</h1>
)
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {})(Profile)
actions.js
export const fetchUser = (.....) => dispatch => {
fetch()
.....
}
reducers.js
case FETCH_USER:
return {
...state,
user: action.payload.user
};
As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.
After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md
It looks like things can be simplified a bit without using classes.
If I were to change the User and Profile components to a more functional way, how would I do it?
eg.
const User = () => {
return (
<Profile />
)
}
how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?
or am I just over complicating things?
There is also a way to support lifecycle methods in functional components.
https://www.npmjs.com/package/react-pure-lifecycle
import React from 'react';
import lifecycle from 'react-pure-lifecycle';
// create your lifecycle methods
const componentDidMount = (props) => {
console.log('I mounted! Here are my props: ', props);
};
// make them properties on a standard object
const methods = {
componentDidMount
};
const FunctionalComponent = ({children}) => {
return (
<div>
{children}
</div>
);
};
// decorate the component
export default lifecycle(methods)(FunctionalComponent);
I think you should keep using statefull components with redux...
https://medium.com/#antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.
Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}
componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.
So — something inside pure components is very soiled and absolutely impure. It is driven by side effects!
But this bypass straight logic flow, and works just differently from the rest of application.
So — just be careful. And don’t forget about magic.
Aaaand….
And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
redux-connect is a source of a change propagation.
redux connect is the end of a change propagation. It is still PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.
Stateless components are not pure, and may run slower than PureComponents by any kind.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component.
No choice. But, now — you know hidden truth, and knew some magic…
React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.
Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.
If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.
If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples
In Your case you can continue with statefull components no wrong in that
,If you need to go with functional way
There is a work arround
https://github.com/mobxjs/mobx/issues/162
Suggestion
Calling the api in componentDidMount will make sense than
componentWillMount , Because you can show the user something is
fetching.
I think,User component is designed nicely.It will act as a container for Profile to provide the Data.
Instead of making Profile component class oriented,it should be Stateless.
Lets User component pass the required data for Profile component.
You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.
Profile
const Profile = (props) => {
const {user, likeProfile} = props;
//likeProfile()//call like this using dom event or programmatically.
return (
<h1>{user.profile.name}</h1>
)
}
You need to make some changes in User component.
Get the state for Profile component via mapStateToProps.
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
const {user, likeProfile} = this.props;
return (
<Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
)
}
Map the user state for Profile in User connect.
const mapStateToProps = (state)=>{
return{
user : state.somereducerkey.user //this will be accessible in Profile via props { user}
}
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);