如何使用相同的Redux Saga为多个具有自己状态的React组件?

时间:2017-09-20 18:09:54

标签: reactjs redux react-redux redux-saga

这是一个冗长的解释。尽可能简短:我有两个组件EmployeesAddEmployee - 它们分别附加到路由/employeesemployees/addEmployee组件中的链接会将employees/add推送到BrowserHistory,反之亦然(AddEmployee组件也会在表单成功提交后推送/employees,或者如果是用户点击'取消')。

如果我导航到/employees,组件工作正常。如果我点击导航到/employees/add 组件正常工作。如果我点击“取消”或提交表单以重定向回/employees ...组件会中断。

通过大量调试,我确定这是因为AddEmployee的redux状态持续到Employee。我的猜测是因为他们都使用相同的传奇。我编写了一个get Redux saga来处理对远程API的请求。 Employees显然使用get传奇来检索所有用户,而AddEmployee使用get传奇来检索可能分配给用户的位置。

加载Employees时,apiGet saga的状态包含检索到的员工。加载AddEmployee时,apiGet saga的状态包含检索到的位置。然后,当您将返回导航到Employees时,apiGet saga 的状态仍然包含检索到的位置,并且它不会再次运行以加载员工再次。

我该如何解决这个问题?

我已尽力将下面的代码压缩到相关部分。

get佐贺:

function getApi(endpoint, token) {
    return fetch(apiUrl + endpoint, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + token
        }
    }).then(handleApiErrors)
      .then(response => response.json())
      .catch((error) => {throw error})
}

function* apiGetFlow(endpoint) {
    let response, token
    try {
        token = localStorage.getItem('token')
        token = JSON.parse(token)

        response = yield call(getApi, endpoint, token)

        yield put({ type: CLIENT_GET_SUCCESS, response: response })

    } catch(error) {
        ...
    }

    return response
}

function* apiGetWatcher() {
    while(true) {
        const { endpoint } = yield take(CLIENT_GET_REQUESTING)
        const task = yield fork(apiGetFlow, endpoint)
    }
}

export default apiGetWatcher

Employees组件:

class Employees extends Component {

    componentDidMount() {
        this.usersGet()
    }

    usersGet() {
        this.props.apiGetRequest('users')
    }

    render() {
        ...
    }
}

const mapStateToProps = state => {
    return {
        users: state.apiGet,
    }
}

const connected = connect(mapStateToProps, { apiGetRequest })(Employees)

export default connected

AddEmployee组件:

class AddEmployeeForm extends Component {

    componentDidMount() {
        this.locationsGet()
    }

    locationsGet() {
        this.props.apiGetRequest('locations')
    }

    render() {
        ...
    }
}

const mapStateToProps = state => {
    return {
        locations: state.apiGet,
        save: state.apiPost,
    }
}

const formed = reduxForm({
    form: 'addemployee',
})(AddEmployeeForm)

const connected = connect(mapStateToProps, { apiGetRequest, apiPostRequest })(formed)

export default connected

1 个答案:

答案 0 :(得分:0)

我怀疑它是因为你的React组件没有卸载。看起来这些组件从componentDidMount生命周期方法中激活了它们的api请求。

假设您从/employees/开始,Employees组件已安装并为用户分派api请求。

当您导航到/employees/add时,AddEmployeeForm组件会安装并调度位置的api请求。

当您向后按,现在回到/employees时,Employees组件已经安装。因此,它不会再次针对用户'

发起请求。

您可以通过使用console.log在每个组件中实现componentWillUnmount方法来验证这是否是问题。

话虽如此,在我看来,你不应该在期望数据完全不同的组件之间共享state.apiGet缩减器。不同的数据应该在我们的减速器中保持状态。

假设您重新构建了组件,以便在来回时成功分派API请求,并state.apiGet进行相应更新。这真的是你想要的吗?每次用户按下前进和后退时,API响应会不同吗?此外,api请求会有延迟,state.apiGet最初会有错误的数据,直到请求完成。

我会创建一个users减速器和一个locations减速器。然后创建两个包含getApi方法的传奇,并发送他们的特定操作。

例如:

function* getUsers(endpoint) {
    let response, token
    try {
        token = localStorage.getItem('token')
        token = JSON.parse(token)

        response = yield call(getApi, endpoint, token)

        yield put({ type: 'ADD_USERS', response: response })

    } catch(error) {
        ...
    }

    return response
}

并且

function* getLocations(endpoint) {
    let response, token
    try {
        token = localStorage.getItem('token')
        token = JSON.parse(token)

        response = yield call(getApi, endpoint, token)

        yield put({ type: 'ADD_LOCATIONS', response: response })

    } catch(error) {
        ...
    }
    return response
}

在您的组件中:

const mapStateToProps = state => {
    return {
        users: state.users,
    }
}

const mapStateToProps = state => {
    return {
        locations: state.locations,
        save: state.apiPost,
    }
}

Official Redux文档中有关于如何创建单独的缩减器的详细信息。