中继重新获取失败...错误中继无法协调连接上的边缘

Relay re-fetching failed... error Relay was unable to reconcile edges on a connection

请帮忙!也许有人已经遇到过我的问题....

当使用更新后的变量重新获取数据时——使用relay.setVariables()——我收到错误"Relay was unable to reconcile edges on a connection. This most likely occurred while trying to handle a server response that includes connection edges with nodes that lack anid field." 尽管数据正确,但中继存储未更新。

查看下面的代码... (Ruby && ES6)

gemfile

gem 'graphql', '0.16.0'
gem 'graphql-relay', '0.11.2'

.....服务器端代码

node_identification.rb

NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
object_from_id -> (id, ctx) do
    type, id = NodeIdentification.from_global_id(id)
    case type
        when 'FrontApp'
            Relay::FrontApp::STATIC
        else
            Object.const_get(type).find_by(id: id)
    end
end

type_from_object -> (obj) do
    begin
        MODEL_TO_TYPE[obj.class.name.to_sym].constantize
    rescue
        (obj.class.name + 'Type').constantize
    end
end
end

MODEL_TO_TYPE = {
   :'Relay::FrontApp' => 'FrontAppType'
}

front_app_query_type.rb

FrontAppQueryType = GraphQL::ObjectType.define do
name 'FrontAppRootQuery'
field :node, field: NodeIdentification.field

field :main, FrontAppType do
    resolve -> (obj, args, ctx) {
        Relay::FrontApp::STATIC
    }
end
end

front_app_type.rb

FrontAppType = GraphQL::ObjectType.define do
name 'FrontApp'
# field :node, field: NodeIdentification.field
interfaces [NodeIdentification.interface]
global_id_field :id

field :tips, TipsType do
    argument :filters, types.String
    resolve -> (obj, args, ctx) {
        filters = args[:filters]
        begin
            filters = JSON.parse(filters).deep_symbolize_keys!
        rescue
            filters = nil
        end
        ctx[:filters] = filters
        Relay::Tips::STATIC
    }
end
connection :footer, FooterMenuItemType.connection_type do
    argument :id, types.ID!
    resolve ->(obj, args, ctx){
        ::Footer.order(:id)
    }
end
end

tips_connection_type.rb

TipsConnectionType = TipShowType.define_connection do
field :totalCount, types.Int do
    resolve -> (obj, args, ctx) {
        obj.object.size
    }
end
end


TipsType = GraphQL::ObjectType.define do
name 'Tips'
description 'Tips list for home page'
interfaces [NodeIdentification.interface]
global_id_field :id

connection :mostRecent, TipsConnectionType do
    argument :limit, types.Int
    resolve ->(obj, args, ctx){
        tips = ::Tip.active_users.started(Time.zone.now.in_time_zone(ctx[:current_user] ? ctx[:current_user].time_zone : ::User.get_locally_time_zone).to_date).ready.active.moderated.published.includes(:comments, :tip_type).order('created_at desc').limit(args[:limit])
        TipHelpers::Filter.filter(tips: tips, filters: ctx[:filters], reorder: 'created_at desc')
    }
end
connection :mostPopular, TipsConnectionType do
    argument :limit, types.Int
    resolve ->(obj, args, ctx){
        tips = ::Tip.active_users.started(Time.zone.now.in_time_zone(ctx[:current_user] ? ctx[:current_user].time_zone : ::User.get_locally_time_zone).to_date).ready.active.moderated.published.includes(:comments, :tip_type).order('(SELECT COUNT(*) FROM comments WHERE tid = tips.id) desc').limit(args[:limit])
        TipHelpers::Filter.filter(tips: tips, filters: ctx[:filters], reorder: '(SELECT COUNT(*) FROM comments WHERE tid = tips.id) desc')
    }
end
end

/app/models/relay/front_app.rb

module Relay
class FrontApp < Struct.new :id
    # HACK::// For relay root queries
    STATIC = new(id: 'main').freeze

    def initialize *args
        opts = args.last.is_a?(Hash) ? args.pop : Hash.new
        super *args
        opts.each_pair do |k, v|
            self.send "#{k}=", v
        end
    end

    def self.find(_)
        STATIC
    end
end
end

** /app/models/relay/tips.rb**

module Relay
class Tips < Struct.new :id
    # HACK:// For relay root queries
    STATIC = new(id: 'tips').freeze

    def initialize *args
        opts = args.last.is_a?(Hash) ? args.pop : Hash.new
        super *args
        opts.each_pair do |k, v|
            self.send "#{k}=", v
        end
    end

    def self.find(_)
        STATIC
    end

end
end

...和客户端代码

class MainApp extends React.Component {

constructor(props) {
    super(props);

    this.state = {
        filters: filtersTemplate
    };

    this.setFilter = this.setFilter.bind(this);
}

setFilter(filter, value, e) {
    if (e) {
        e.nativeEvent.stopImmediatePropagation();
        e.preventDefault();
    }

    let { filters } = this.state;
    if (['currency', 'sum'].inArray(filter)) {
        filters.funds[filter] = value;
    } else {
        filters[filter] = value;
    }
    this.setState({
        filters: filters
    });
    this.props.relay.setVariables({filters: filters});
}

render() {
    let { tips } = this.props.main;
    let renderTipsSection = (section) => {
        let tipsCount = tips[section] ? tips[section].edges.length : 0;
        let blankCount = 10 - tipsCount;
        return (
            <ul className="tips__list">
                {
                    tips[section] && tips[section].edges.map(({node}) => (
                        <li key={node.id} className="tips__list_item">
                            <TipCard node={node}/>
                        </li>
                    ))
                }
                {
                    [...new Array(blankCount).keys()].map((item, idx) => (
                        <li key={idx} className="tips__list_item">
                            <TipCard dummy={true}/>
                        </li>
                    ))
                }
            </ul>
        );
    };
    return (
        <div>
            <div className="wrapper">
                <div className="TipsWrapper">
                    <div className="wrapper">

                        { renderTipsSection('mostRecent') }

                        { renderTipsSection('mostPopular') }

                    </div>
                </div>
            </div>
        </div>
    );
}}

export default Relay.createContainer(MainApp, {
initialVariables: {
    filters: {
        category: null,
        funds: {
            currency: null,
            sum: null
        },
        date: null,
        location: null,
        browse: null
    }
},
prepareVariables: (prevVars) => {
    return {
        ...prevVars,
        filters: JSON.stringify(prevVars.filters)
    }
},
fragments: {
    main: () => Relay.QL`
        fragment on FrontApp {
            tips(filters: $filters) {
              mostRecent(first: 10, limit: 10){
                edges {
                  node {
                    id
                    tid
                    title_name
                    category
                  }
                }
              }  
              mostPopular(first: 10, limit: 10){
                edges {
                  node {
                    id
                    tid
                    title_name
                    category
                  }
                }
              }  
            }
        }
    `
}});

当 setFilter() 触发时,将调用 relay.setVariables... 结果...

[RELAY-NETWORK] Run query q3 Object {relayReqId: "q3", relayReqObj: RelayQueryRequest, relayReqType: "query", method: "POST", headers: Object…}
[RELAY-NETWORK] query q3: 3429ms
Warning: Relay was unable to reconcile edges on a connection. This most likely occurred while trying to handle a server response that includes connection edges with nodes that lack an `id` field

经过几个小时的折磨,找到了上述问题的解决方案...见下文

解决方案

客户端突变

export default class ApplyFiltersMutation extends Relay.Mutation {
static fragments = {
    tips: () => Relay.QL`
        fragment on Tips { 
            id 
        }
    `,
};

getMutation() {
    return Relay.QL`mutation {
        applyFilters
    }`;
}

getVariables() {
    return {
        filters: this.props.filters
    };
}

getFatQuery() {
    return Relay.QL`
        fragment on ApplyFiltersPayload {
            tips
        }
    `;
}

getConfigs() {
    return [
        {
            type: 'FIELDS_CHANGE',
            fieldIDs: {tips: this.props.tips.id},
        }
    ];
}
}

服务器端

home_mutations.rb

module HomeMutations
  ApplyFilters = GraphQL::Relay::Mutation.define do
    name 'ApplyFilters'
    input_field :filters, !types.String

    return_field :tips, BipsType

    resolve -> (args, ctx) {
        filters = args[:filters]
        begin
            filters = JSON.parse(filters).deep_symbolize_keys!
        rescue
            filters = nil
        end
        ctx[:filters] = filters

        {
            tips: Relay::Tips::STATIC
        }
    }
  end
end

tips_type.rb

include TipHelpers::Filter

TipsType = GraphQL::ObjectType.define do
name 'Tips'
description 'Tips list for home page'
interfaces [NodeIdentification.interface]
global_id_field :id

connection :almostRaised, TipsConnectionType do
    resolve ->(obj, args, ctx){
        TipHelpers::Filter.filter(section: 'almost_raised', filters: ctx[:filters], current_user: ctx[:current_user])
    }
end

...

end
end

lib/tip_helpers.rb

class TipHelpers
module Filter

    def filter(section:, filters:, current_user:)

        ...

        # p tips.reorder(reorder).to_sql
        tips.reorder(reorder)
    end
end
end