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>
Related
I was always working with React / Redux-Saga, recently I started working with recompose, It made me a little bit confused, I am still learning how to deal with data in this new approach to me, so before if I want to get data from my redux state and pass it to local state I do something like this :
class Comp extends Component {
constructor(props){
super(props);
this.state = {
data: this.props ? this.props.data :
}
}
// Or I can use componentWillReceive Props method to get the props and use setState to make my local state get data
componentWillReceiveProps(props){
this.setState({data: props.data});
}
render(){
return (my UIs)
}
}
function mapStatToProps(state){
return { data: state.data }
}
connect(mapStateToProps, null)(Comp);
Now I have to use an other architecture when I separate logic from data, a component that receives data and handle it ( Container ), and other just reading the data for most of the time.
I have to do something like this :
function mapStatToProps(state){
return { data: state.data }
}
const CompContainer = compose(
connect(mapStateToProps, null),
withState('data', ??, null ), // I don't know how to push data my props to here before rendering UI
withProps(({data}) => ({data}))
)(CompUI);
And the CompUI has only jsx elements, or maybe it will have some events that I will execute by adding the withHandlers in my container.
As I mentionned in the comment in the second piece of code, I don't know how to pass data know to this component localState.
How can I achieve this ? Or there is now way to do this because we have to work only with functionnal components with this approach ?
Any help would be much appreciated.
I have a react.js typescript app, where I have a component (OrganizationsSearch) that receives its props from a parent component (Organizations), which in turn receives its props from redux.
In the OrganizationsSearch component, i initialize state like this:
export default class OrganizationsSearch extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
filteredOrganizations: this.props.organizations,
filterQuery: ""
}
}
...
The problem is when i navigate to the component through my route setup, the filteredOrganizations state is not equal to props.organizations. filteredOrganizations is just an empty array.
As can be seen in the code above, i have a filterQuery state property. This property is attached to an input field. Whenever i change the input, a filter function is run that updates the state based on this property. When the function is triggered, the state is correctly updated to contain the organizations that match the query. If i delete the input field content so that it's equal to the initial state value, the filteredOrganizations state contain all of the organizations as it should also do initially on component load.
What do i have to do to set the initial state to be equal to the props?
I think your problem could be that when the constructor of the OrganizationsSearch runs there is no data in this.props.organizations. You can easily check this with a console.log.
You could fix it that you only diplay the OrganizationsSearch component when the organization data is ready.You can display a Loading... text until then or some kind of progress indicator.
render() {
...
{organization ? <OrganizationsSearch organization={organization} /> : <div>Loading...</div>}
...
}
Another approach can be, that you do not store the filteredOrganization in the state. You can create it on the fly from organization and the filter value for the renderer. So there is only a single source of truth and you can avoid data inconsistencies. Storing prop data in state is discouraged anyway.
I think it might be about your component lifecycle.
The constructor is only called when the component is .. constructed. So depending on when redux sets your props, it might be too late, your component has already been "constructed" with an empty prop.
You can learn more in this kind of article https://blog.bitsrc.io/understanding-react-v16-4-new-component-lifecycle-methods-fa7b224efd7d
TL;DR you cant use the static function getDerivedStateFromProps for exemple :
static getDerivedStateFromProps(props, state) {
if (state.value !== props.value) {
return {
derivedValue: deriveValueFromProps(props),
mirroredProp: props.value
}
}
// when null is returned no update is made to the state
return null;
}
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.
We started using the react-apollo#2.1 in one of our react-redux application and we are trying to replace most of the redux stuff with graphql. So we started using the <Query> component and converted some pages to use the <Query> component. But in some pages, we have some existing logic which uses react lifecycle events like componentWillReceiveProps and if we used the <Query> component then those events won't get fired and if we use HOC type querying like below then the lifecycle events get fired and we will get the data in the props
export default graphql(GET_APP_INFO, {
options: ownProps => ({
variables: { appName: ownProps.params.app }
}) })
My understanding is that the component is the latest way to query and HOC is the old way which may get deprecated, also I saw some other way to query with withApollo() like below.
this.props.client.query({
query: gql...,
variables: { ... },
});
So I am looking for suggestions on when to use these different types of querying patterns to get the data
The Query component and the graphql HOC have the same basic functionality, so there shouldn't be a distinction from that perspective. Your example of the Query component not running lifecycle methods, you could move the Query component up one level out of the component, and it would work exactly the same. So something like this:
const CompQueryWrapper = () => {
return (
<Query ...>
{({ data, loading, error }) => <CompWithLifecycle data={data} />}
</Query>
}
You also might be able to move the lifecycle logic down a level. reactions/component can sometimes be handy in this case, when you just want something to trigger on a props change but don't want to go through the ceremony of making a new class component.
So yeah, for your specific situation, my recommendation would be to move the Query up so that it triggers the lifecycle or move the lifecycle logic down so it can be triggered by the Query result.
In general, the two components that I use for making queries are
Query Component
ApolloConsumer (it's basically just a render-prop version of withApollo)
If I can use the Query component, I use the Query component. It works for most situations. The use-case for ApolloConsumer for querying is when you don't want to immediately trigger a query. For example, say you have a form that requires some info from the user and then gets some data based on that input. It's not a mutation, since we're not changing any data, but we don't want it to fire immediately either like the Query component does. In this case, use ApolloConsumer to get the client instance, and fire the query using client.query.
if you want use React-Apollo and run Query Dynamic , for example inside ReactLifeCycleMethod or in any other method ( for some case , when any Event listener trigger ) , you cane use Client for access Query or Mutation
React-Apollo have Consumer , with React Context API
in Top Level of your Application you implement ApolloProvider like this
<ApolloProvider client={client} >
< App />
</ApolloProvider>
and now you have access client
you can create Query with Client
client.query()
or
client.mutate()
and if you want use client in other component you must use ( like react new Context API)
class App extends React.Component {
render() {
return(
<ApolloConsumer >
{ client => {
<MyComponent accessClient={client} />
} }
<ApolloConsumer>
)
}
}
now in you can access client as props
class App extends React.Component {
componentDidMount() {
this.props.accessClient.query( ... )
}
render() {
return(
....
)
}}
https://www.apollographql.com/docs/react/essentials/queries.html#manual-query
I have 3 containers:
UserTask
UserMsg
UserNotify
Each have their own page in the app, but on the main page there is a drop down displaying all container data. Here is how I decorated the dropdown component:
#UserTaskContainer
#UserMessageContainer
#UserNotifyContainer
class ActivitiesDropdown extends React.Component {
....
componentWillMount() {
const allProps = this.props;
}
....
}
exports.ActivitiesDropdown = ActivitiesDropdown;
When debugging, all load data successfully and I see each's data being added to state, but in the ComponentWillMount, only the data from Notification state is present.
It appears, if I switch the decorators around, which ever is the last decorator is the one that shows up in props.
Question:
If I have a component that has multiple containers, how can I use the decorators to get all the states' props, or is there another way to acheive this?
Thanks in Advance.