【问题标题】:How to reuse the same Redux Saga for multiple React components with their own state?如何为具有自己状态的多个 React 组件重用相同的 Redux Saga?
【发布时间】:2018-03-01 22:13:45
【问题描述】:

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

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

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

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

我该如何解决这个问题?

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

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

【问题讨论】:

    标签: reactjs redux react-redux redux-saga


    【解决方案1】:

    我怀疑这是因为您的 React 组件没有卸载。看起来组件从 componentDidMount 生命周期方法内部触发了它们的 api 请求。

    假设你从/employees/开始,Employees组件为用户挂载和调度api请求。

    当您导航到/employees/add 时,AddEmployeeForm 组件会挂载并分派位置的 api 请求。

    当您按下返回时,您现在返回到/employeesEmployees 组件已经挂载。因此它不会再次为“用户”触发请求。

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

    话虽如此,在我看来,您不应该在期望数据完全不同的组件之间共享state.apiGet reducer。不同的数据应该在我们的 reducer 中维护。

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

    我会创建一个users 减速器和一个locations 减速器。然后创建两个 sagas 来包装您的 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 文档中有关于如何创建单独的减速器的详细信息。

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 2018-07-17
      • 2019-10-24
      • 2016-05-29
      • 1970-01-01
      • 2021-05-21
      • 2017-02-22
      • 1970-01-01
      • 2019-09-14
      相关资源
      最近更新 更多