在axios获取和存储中,通过ID将来自不同api的两个json对象连接起来

时间:2019-07-17 18:55:51

标签: reactjs axios mobx mobx-react

我有一个关于如何在axios中添加第二个嵌套api查询的问题。第二个api查询基于第一个api数组json中的id获取json对象。然后在函数Retrieve()中连接到全局数组?

第一个api网址:

'/api/sets'

第二个api子级:

'/api/sets/' + todo.id + '/tasks'

从第一个API网址响应全局json:

[
    {
        "id": 121,
        "name": "list1",
        "description": "description1"
    },
    {
        "id": 9,
        "name": "list2",
        "description": "description2"
    }
]

通过ID第一API响应来自API的第二个JSON子级:

[
    {
        "id": 1,
        "name": "task1",
        "description": "description task1"
    },
    {
        "id": 2,
        "name": "task2",
        "description": "description task2"
    }
]

,最后期望合并的存储数组json

[
     {
        "id": 121,
        "name": "list1",
        "description": "description1",
        "task": [{
                "id": 1,
                "name": "task1",
                "description": "description task1"
            },
            {
                "id": 2,
                "name": "task2",
                "description": "description task2"
            }
        ]
    },
    {
        "id": 9,
        "name": "list2",
        "description": "description2",
        "task": [{
                "id": 10,
                "name": "task1",
                "description": "description task1"
            },
            {
                "id": 11,
                "name": "task2",
                "description": "description task2"
            }
        ]
    }
]

代码js: index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'mobx-react';
import TodoStore from './store';

const Root = (
    <Provider TodoStore={TodoStore}>
        <App />
    </Provider>
);

ReactDOM.render(Root, document.getElementById('root'));

store.js

import React from 'react';
import { observable, action, configure, runInAction } from 'mobx';
import axios from 'axios';
configure({ enforceActions: 'observed' });

class TodoStore {
    @observable todos = [];

    @action Retrieve = () => {
        axios
            .get('/api/sets')
            .then(response => {
                let tempTodos = response.data;
                runInAction(() => {
                    this.todos = tempTodos;
                });
            })
            .catch(error => {
                console.log(error);
            });
    };
}

const store = new TodoStore();
export default store;

app.js

import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { toJS } from 'mobx';
import { inject, observer } from 'mobx-react';

@inject('TodoStore')
@observer
class App extends Component {
    render() {
        const TodoStore = this.props.TodoStore;
        console.log(toJS(TodoStore.todos));

        return (
            <div className="App">
                <div className="Todo-container">
                    {TodoStore.todos.map(todo => (
                        <TodoItem key={todo.id} todo={todo} />
                    ))}
                </div>
            </div>
        );
    }

    componentDidMount() {
        this.props.TodoStore.Retrieve();
    }
}

export default App;

TodoItem.js

import React from 'react';
import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';

const TodoItem = inject('TodoStore')(
    observer(props => {
        const TodoStore = props.TodoStore;

        return (
            <>
                <div key={props.todo.id} className="todo-item">
                    <span>{props.todo.id}</span>
                    <h5>{props.todo.name}</h5>
                    <p>{props.todo.description}</p>

                    <ul className="list-item">
                        <li>list-item id?</li>
                        <li>list-item id?</li>
                        <li>list-item id?</li>
                    </ul>
                </div>
            </>
        );
    })
);

TodoItem.wrappedComponent.propTypes = {
    todo: PropTypes.object.isRequired,
    TodoStore: PropTypes.object.isRequired
};

export default TodoItem;

3 个答案:

答案 0 :(得分:0)

如果您的API不支持GraphQL端点,那么您必须扩展Retrive()操作并对结点ID进行额外的XHR请求,以合并来自API端点1和2的结果。

 @action Retrieve = () => {
        axios
            .get('/api/sets')
            .then(response => {
                let tempTodos = response.data;

                let todosWithTasks = tempTodos.map(todo => {

                   let tasks = null;

                    axios.get('/api/sets/' + todo.id + '/tasks')
                      .then(response2 => {
                          tasks = response2.data;
                      }).catch(error => {
                          console.log(error);
                      });

                    todo.task = tasks;
                    return todo;

                });

                // `todosWithTasks` is joined API1 and API2

            })
            .catch(error => {
                console.log(error);
            });
    };

答案 1 :(得分:0)

多谢了,所有api都加入了,现在我还有另一个小问题。如果在控制台日志键@observable.ref todos = [];中设置@observable.shallow todos = [];task,则退出并带有对象数组,但是当我设置@observable todos = [];task时不存在。

console.log

(2) [{…}, {…}]
  0:
    description: "false"
    id: 121
    name: "list1"
    task: Array(2)
      0: {id: 10, name: "task1", description: "description task1", state: false, position: 1}
      1: {id: 11, name: "task2", description: "description task2", state: true position: 2}
      length: 2
      __proto__: Array(0)
  __proto__: Object
  1:
    description: "false"
    id: 9
    name: "list2"
    task: Array(2)
      0: {id: 3, name: "task1", description: "description task1", state: false, position: 3}
      1: {id: 7, name: "task2", description: "description task2", state: false, position: 5}
      length: 2
      __proto__: Array(0)
  __proto__: Object
  length: 2
  __proto__: Array(0)

,如果尝试使用地图键task

index.module.js:206 Uncaught TypeError: Cannot read property 'map' of undefined
    at eval (TodoItem.jsx:17)
    at eval (index.module.js:220)
    at eval (index.module.js:198)
    at trackDerivedFunction (mobx.module.js:1212)
    at Reaction.track (mobx.module.js:1752)
    at useObserver (index.module.js:196)
    at wrappedComponent (index.module.js:220)
    at renderWithHooks (react-dom.development.js:12938)
    at updateFunctionComponent (react-dom.development.js:14627)
    at updateSimpleMemoComponent (react-dom.development.js:14573)

react-dom.development.js:17117 The above error occurred in the <wrappedComponent> component:
    in wrappedComponent (created by n)
    in n (created by inject-with-TodoStore(Object))
    in inject-with-TodoStore(Object) (created by App)
    in div (created by App)
    in App (created by n)
    in n (created by inject-with-TodoStore(App))
    in inject-with-TodoStore(App)
    in e

code index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import { Provider } from 'mobx-react';
import TodoStore from './TodoStore';
import registerServiceWorker from './registerServiceWorker';

const Root = (
    <Provider TodoStore={TodoStore}>
        <App />
    </Provider>
);

ReactDOM.render(Root, document.getElementById('root'));
registerServiceWorker();

TodoStore.js

import React from 'react';
import {observable, action, computed, configure, runInAction} from 'mobx';
import axios from 'axios';
axios.defaults.baseURL = 'api';
configure({ enforceActions: 'observed' });

class TodoStore {
    @observable.shallow todos = [];

    @action Retrieve = () => {
        axios
            .get('/sets')
            .then(response => {
                let tempTodos = response.data;

                let todosWithTasks = tempTodos.map(todo => {
                    let tasks = null;

                    axios
                        .get('/sets/' + todo.id + '/tasks')
                        .then(response2 => {
                            todo.task = response2.data;
                        })
                        .catch(error => {
                            console.log(error);
                        });
                    return todo;
                });
                runInAction(() => {
                    this.todos = todosWithTasks;
                });
            })
            .catch(error => {
                console.log(error);
            });
    };
}

const store = new TodoStore();
export default store;

app.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TodoItem from './TodoItem';
import { toJS } from 'mobx';
import { inject, observer } from 'mobx-react';

@inject('TodoStore')
@observer
class App extends Component {
    render() {
        const TodoStore = this.props.TodoStore;
        console.log(toJS(TodoStore.todos));

        return (
            <div className="App">
                {TodoStore.todos.map(todo => (
                    <TodoItem key={todo.id} todo={todo} />
                ))}
            </div>
        );
    }

    async componentDidMount() {
        this.props.TodoStore.Retrieve();
    }
}
TodoItem.wrappedComponent.propTypes = {
    todo: PropTypes.object.isRequired,
    TodoStore: PropTypes.object.isRequired
};
export default App;

todoitem

import React from 'react';
import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';
import TodoItemTask from './TodoItemtask';

const TodoItem = inject('TodoStore')(
    observer(props => {
        const TodoStore = props.TodoStore;

        return (
            <>
                <div key={props.todo.id} className="todo-item">
                    <span>{props.todo.id}</span>
                    <h5>{props.todo.name}</h5>
                    <p>{props.todo.description}</p>

                    {props.todo.task.map((item, index) => (
                        <TodoItemTask key={index + item.id} item={item} />
                    ))}
                </div>
            </>
        );
    })
);

TodoItem.wrappedComponent.propTypes = {
    todo: PropTypes.object.isRequired,
    TodoStore: PropTypes.object.isRequired
};

export default TodoItem;

TodoItemTask.js

import React from 'react';
import PropTypes from 'prop-types';
import { inject, observer } from 'mobx-react';

const TodoItemTask = inject('TodoStore')(
    observer(props => {
        const TodoStore = props.TodoStore;

        return (
            <>
                <div key={props.item.id} className="todo-item">
                    <span>{props.index}</span>
                    <p>{props.item.name}</p>
                    <p>{props.item.description}</p>
                </div>
            </>
        );
    })
);

TodoItemTask.wrappedComponent.propTypes = {
    item: PropTypes.object.isRequired,
    TodoStore: PropTypes.object.isRequired
};

export default TodoItemTask;

没有错误,如果设置了静态数据,则呈现所有数据

@observable todos = [
     {
        "id": 121,
        "name": "list1",
        "description": "description1",
        "task": [{
                "id": 1,
                "name": "task1",
                "description": "description task1"
            },
            {
                "id": 2,
                "name": "task2",
                "description": "description task2"
            }
        ]
    },
    {
        "id": 9,
        "name": "list2",
        "description": "description2",
        "task": [{
                "id": 10,
                "name": "task1",
                "description": "description task1"
            },
            {
                "id": 11,
                "name": "task2",
                "description": "description task2"
            }
        ]
    }
];

答案 2 :(得分:0)

实际上,这不是mobx的问题,我发现此函数中错误地连接了两个api url json。 在console.log(todosWithTasks)中看起来不错,但是console.log (JSON.stringify(todosWithTasks))却不正确。

这是实际代码

import React from 'react';
import {observable, action, computed, configure, runInAction} from 'mobx';
import axios from 'axios';
axios.defaults.baseURL = 'api';
configure({ enforceActions: 'observed' });

class TodoStore {
    @observable todos = [];

@action async Retrieve = () => {
    this.isLoading = true;
    await axios
        .get('/sets')
        .then(async response => {
            let tempTodos = response.data;
            tempTodos.forEach(todo => (todo.task = []));
            let todosWithTasks = tempTodos.map(todo => {
                axios
                    .get('/sets/' + todo.id + '/tasks')
                    .then(response2 => {
                        todo.task = response2.data;
                        return todo;
                    })
                    .catch(error => {
                        console.log(error);
                    });
                return todo;
            });
            runInAction(() => {
                console.log(todosWithTasks);
                console.log(JSON.stringify(todosWithTasks));
                this.todos = todosWithTasks;
            });
        })
        .catch(error => {
            console.log(error);
        });
   };

}

const store = new TodoStore();
export default store;

console.log(JSON.stringify(todosWithTasks))的输出

[{"id":1,"name":"list1","description":"description1","task":[]}]

console.log(todosWithTasks)的输出看起来很好

(1) [{…}]
    0:
        description: "description1"
        id: 1
        name: "list1"
        task: Array(1)
            0: {id: 1, name: "task1", description: "description task1"}
            length: 1
            __proto__: Array(0)
        __proto__: Object
    length: 1
    __proto__: Array(0)

因此,它不会在map函数中呈现,因为key task为空。

如果修改后的合并json文件为localy,则在axios中一切正常。在console.log(JSON.stringify(todosWithTasks))中,一切正常。

    @action Retrieve() {
        axios
            .get('localhost:3000/src/data.json')
            .then(response => {
                let tempTodos = response.data;
                tempTodos.forEach(todo => (todo.editing = false));

                runInAction(() => {
                    this.todos = tempTodos;
                    console.log(JSON.stringify(tempTodos));
                });
            })
            .catch(error => {
                console.log(error);
            });
    }