Admin-on-rest - how to sync other components with

2019-08-26 03:12发布

问题:

I want to implement this structure in the page:
1. Cards with summary (revenue, users, etc.)
2. Map from google maps
3. List element

Inside List element there is filtering. I am stuck how to user filter options when filtering list to represent filtered information in the map and cards.

As @trixn recommended I am using this structure:

// app.js

<Admin restClient={jsonServerRestClient('http://jsonplaceholder.typicode.com')}>
    <Resource name="posts" list={MyCustomPostList} /* other views */ />
</Admin>


// MyCustomPostList.js
class MyCustomPostList extends React.Component {
render() {
    const {myOwnProp, ...otherProps} = this.props;

    return (
        <div>
            // render your own components here
            <AnyComponent myOwnProp={myOwnProp} />
            <AGoogleMapsComponent />

            // render the normal <List> component
            <List {...otherProps}>
                <Datagrid>
                    <TextField source="id" />
                    <TextField source="title" />
                    <TextField source="body" />
                </Datagrid>
             </List>
        </div>
    );
}
}

Now I am failing to send required data to Maps and AnyComponent. So I either have to pass same data to Maps and AnyComponent or somehow synchornize filters that are being used in the List component.

How shall I achieve this?

回答1:

Thank you! I would know how to connect new actions. But I am lost how to connect to already used actions to pass Filter options to Maps and AnyComponent, so I could show relevant information there, which should update its state after Filters are being triggered in List component.

You do not connect to actions. You dispatch them to alter the state of the redux store. It is important that you fully understand the concepts of redux otherwise it will be very hard for you to build this kind of custom app. To make sure data is only flowing from top to bottom components can only listen to changes of the store and not to actions.

So basically you have two options:

1. Create your own <List> component

Quoted from the admin-on-rest docs:

Admin-on-rest was build with customization in mind. You can replace any admin-on-rest component with a component of your own, for instance to display a custom list layout, or a different edition form for a given resource.

If you only want to display your additional elements in your list view you can wrap the <List> component instead of the <Resource> component. The <List> component receives the entities in a prop "data" and the filtered ids in a prop "ids". You can use that to display it in your own component. E.g. if you want to manage a list of locations and show a google map inside the list component:

import React from 'react';
import { List } from 'admin-on-rest';


const LocationsList = props => (
    // render your own components
    <MyGoogleMapsComponent  data={this.props.data} ids={this.props.ids}>

    // render the admin-on-rest <List> component
    <List {...props}>
        // render the DataGrid
    </List>
);

Then you can pass your custom list to the <Resource> component:

<Admin>
    <Resource name="locations" list={LocationList} />
</Admin>

When you filter your locations in the list the updated query will be passed to your <LocationList> and it will re-render with the new locations. But remember that this will only show your google map inside the list view of your admin page.

2. Connect your component to the redux store.

Do this only if you want your component, e.g. the google map, to be displayed outside of your list view. This one is much more advanced as you will need to learn a lot more about the internals of admin-on-rest. You will have to connect your custom component to the redux store. You can do that with the connect() function from the react-redux package:

// in MyGoogleMapsComponent.js
import React from 'react';
import { connect } from 'react-redux';

const LocationMap = ({locations}) => (
    //render something based on the props
);

const mapStateToProps = (state, props) => {
    // state.admin contains all the registered resources with their name as a key
    const locationsResource = state.admin.locations;

    // every resource has a "data" object with all entities mapped by id
    const allLocations = locationsResource.data;        

    // every resource has a "list" object that has an array of ids of the currently filtered entities
    const filteredIDs = locationsResource.list.ids;

    return {
        locations: filteredIDs.map(id => allLocations[id]),
    };
};

// connect your component to the store
export default connect(mapStateToProps)(LocationsList)

mapStateToProps is a function that takes the current state in your store and the props of your component and returns an object containing additional props to be passed to your component.

Also this approach uses internals of the implementation of the admin-on-rest components. Some of the props you'll need to use are not part of the api and could in the worst case suddenly change in which case your app may not work anymore if you update to a new version of admin-on-rest. So keep in mind you may need to update your implementation when breaking changes occur.

If you only want to access the filters itself, they are stored in every resource under yourResourceName.list.params.filter with the name of the filter as the key and the value as the value...

Hint: If you want to see, how data inside the store of an admin-on-rest app ist stored in a real life example install the Redux DevTools for google chrome and open the admin-on-rest demo. You can then open the inspection bar and there will be a new tab redux where you can see the contents of the redux store and all the actions that get dispatched when interacting with the app. This way you it will be much easier to understand how admin-on-rest works.



回答2:

The List component exported by admin-on-rest is connected with redux and receive the filters from state. You should do the same. See the code of the List component, espcially the mapStateToProps function:

The List component exported by admin-on-rest is connected with redux and receive the filters from state. You should do the same. See the code of the List component, espcially the mapStateToProps function:

Something like:

// MyCustomPostList.js
import { connect } from 'react-redux';
import { parse } from 'query-string';

class MyCustomPostList extends React.Component {
    render() {
        const { myOwnProp, query: { filter }, ...otherProps } = this.props;

        return (
            <div>
                // render your own components here
                <AnyComponent filter={filter} myOwnProp={myOwnProp} />
                <AGoogleMapsComponent filter={filter} />

                // render the normal <List> component
                <List {...otherProps}>
                    <Datagrid>
                        <TextField source="id" />
                        <TextField source="title" />
                        <TextField source="body" />
                    </Datagrid>
                </List>
            </div>
        );
    }
}

const getLocationSearch = props => props.location.search;
const getQuery = createSelector(
    getLocationSearch,
    (locationSearch) => {
        const query = parse(locationSearch);
        if (query.filter && typeof query.filter === 'string') {
            query.filter = JSON.parse(query.filter);
        }
        return query;
    },
);

function mapStateToProps(state, props) {
    return {
        query: getQuery(props),
    };
}

export default connect(mapStateToProps)(MyCustomPostList);