代码之家  ›  专栏  ›  技术社区  ›  Emu

如何在react中从子组件调用调度操作?

  •  0
  • Emu  · 技术社区  · 7 年前

    我有一个 App 组件,a Login 组成部分 在“我的登录”页面中,当用户成功提供凭据时,API将用户数据作为响应返回。我想将该用户数据存储为全局状态。这就是为什么我用 redux

    我的 指数js公司 文件内容:

    const saveUserData = (state = {
      user: {}
    }, action) => {
      switch (action.type) {
        case "UPDATE_USER":
          state = {
              ...state,
              user: action.payload
          };
          break;
      }
      return state;
    };
    
    const store = createStore(saveUserData);
    
    store.subscribe( () => {
      console.log(state.getState());
    });
    
    render(
        <Provider store={store}><App /></Provider>
        , window.document.getElementById('app'));
    

    在我的 应用程序。js公司 文件:

    class App extends React.Component {
      render() {
        return (
            <BrowserRouter>
              <Root>
                <Route exact path="/" component={Home}/>
                <Route exact path="/login" component={Login}/>
                <Route exact path="/register" component={Register}/>
                <Route exact path='/books' component={Books}/>
                <Route path='/books/:id' component={BookDetail}/>
              </Root>
            </BrowserRouter>
        );
      };
    }
    
    const mapStateToProps = (state) => {
      return {
        userData: state.saveUserData
      }
    };
    
    const mapDispatchToProps = (dispatch) => {
      return {
        updateUser: (user) => {
          dispatch({
            type: "UPDATE_USER",
            payload: user
          })
        }
      }
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(App)
    

    在我的 登录名。js公司 文件:

    handleSubmit(event) {
        event.preventDefault();
        if (!event.target.checkValidity()) {
          return;
        }
    
        const {email, password, remember_me} = this.state.formData;
        const url = api.api_url + "auth/login";
    
        fetch(url, {
          method: 'POST',
          headers: new Headers({
            Accept: 'application/json',
            'Content-Type': 'application/json',
          }),
          body: JSON.stringify({
            email: email,
            password: password,
            remember_me: remember_me
          }),
        })
        .then(res => res.json())
        .catch(error => console.error('Error:', error))
        .then(response => {
          if (typeof response.access_token == "undefined")
            this.setState({display_errors: true, errors: response.error.user_authentication});
          else{
            localStorage.setItem('jwtToken', response.access_token);
            this.props.updateUser(response.user); // Here I want to call the dispatcher to save the response user data in global store
            this.props.history.push({
              pathname: '/',
              state: {message: response.message}
            });
          }
        });
      }
    

    在登录组件中,我想调用 应用程序 组件的调度程序方法 updateUser 具有 this.props.updateUser(response.user); .但它不起作用。

    有人知道我怎样才能做到这一点吗?您可以在中看到完整代码 github

    5 回复  |  直到 7 年前
        1
  •  1
  •   MontyGoldy    7 年前

    我会这样做的。首先,你不需要连接你的应用程序。js存储,因为它不使用任何状态或调度操作。使其成为功能组件。

    const App = () => {
      render() {
        return (
            <BrowserRouter>
              <Root>
                <Route exact path="/" component={Home}/>
                <Route exact path="/login" component={Login}/>
                <Route exact path="/register" component={Register}/>
                <Route exact path='/books' component={Books}/>
                <Route path='/books/:id' component={BookDetail}/>
              </Root>
            </BrowserRouter>
        );
      };
    }
    
    export default App;
    

    最佳做法是在switch语句中添加默认情况。

    const saveUserData = (state = {
      user: {},
      display_errors: false,
      errors: {}
    }, action) => {
      switch (action.type) {
        case "UPDATE_USER":
          state = {
              ...state,
              user: action.payload
          };
         case "DISPLAY_ERRORS":
          state = {
              ...state,
              errors: action.payload,
              display_errors: true
          };
         default: 
         return state;
      } 
    };
    

    创建一个单独的操作文件,并将提取请求移动到操作中。js公司

    export default updateUser = (email, password, remember_me) => dispatch => {
    fetch(url, {
          method: 'POST',
          headers: new Headers({
            Accept: 'application/json',
            'Content-Type': 'application/json',
          }),
          body: JSON.stringify({
            email: email,
            password: password,
            remember_me: remember_me
          }),
        })
        .then(res => res.json()).
        .then(response => {
      if (typeof response.access_token == "undefined")
        dispatch({type: DISPLAY_ERRORS, payload: response.error.user_authentication });
      else{
        localStorage.setItem('jwtToken', response.access_token);
    
      }
    });
        .catch(error => console.error('Error:', error))
    
    }
    

    然后,您可以导入此操作文件,并可以在任何需要使用的地方访问updateUser。

    import { updateUser } from "action.js";
    
    //now you have access to this function 
    this.props.updateUser(email, password, remember_me);
    const mapStateToProps = state => ({
      //state.....
    })
    export default connect(mapStateToProps, { updateUser })(Login);
    
        2
  •  0
  •   Munim    7 年前

    先做几件事。

    saveUserData 是你的减速机,为什么它应该暴露在 props ?只有来自与组件相关的州的数据才能通过使用 mapStateToProps 。例如,如果您的用户数据如下 currentUser 在组件中实际使用的状态下,您应该-

    const mapStateToProps = (state) => {
      return {
        currentUser: state.currentUser
      };
    };
    

    现在,在组件内部,您可以- this.props.currentUser 获取数据。

    接下来重要的一点是,您需要使用 bindActionCreators 从…起 redux .定义中的操作 actions.js 然后在组件文件中-

    import {updateUser} from 'path/to/actions.js'
    import {bindActionCreators} from 'redux';
    ...
    
    const mapDispatchToProps = (dispatch) => {
      return bindActionCreators({updateUser}, dispatch);
    };
    

    最后-

    export default connect(mapStateToProps, mapDispatchToProps)(Login);

    编辑:

    已检查您的github repo。我认为第一个问题是进口 Login 组件组件组件 {Login} 在里面 App.js ,应该是 import Login 而是作为默认值从导出 Login.js

    第二件事是使用的“渲染中的组件”属性 Route ,如下所示- <Route exact path="/login" render={() => (<Login/>)}/>

    这为我解决了问题,所以你也应该这样做。

        3
  •  0
  •   Community CDub    5 年前

    理想情况下,您应该存储 updateUser 方法,并在需要使用该方法的地方导入该方法。

    行动。js公司

    export const updateUser = (user) => {
      dispatch({
        type: "UPDATE_USER",
        payload: user
      })
    }
    

    然后,无论您想在哪里使用它,您都可以使用:

    import { updateUser } from 'path/to/actions.js'
    import { bindActionCreators } from 'redux'; 
    
    ...
    
    const mapDispatchToProps = (dispatch) => {
      return bindActionCreators({
        updateUser
      }, dispatch);
    };
    

    当你发现你有一大堆的动作,当你的应用程序在规模和复杂性上扩展时,你可能会有这些动作,这种方法将帮助你正确地管理你的动作。

    始终考虑 Separation of Concerns -您的 行动。js公司 文件将包含您的所有操作,而不是其他操作,并且它放在这里比放在 应用程序。js公司 文件然而,更进一步,我个人会有一个 行动 文件夹中,并将此方法包含在 使用者js公司 该目录中的文件。

    我希望这有帮助。

    编辑

    我添加了 bindActionCreators mapDispatchToProps .对不起,我已经有一段时间没有这样做了-我个人出口 store.dispatch 从我的 应用程序。js公司 在需要的地方导入并使用,而不是使用 mapDispatchToProps 。如果您愿意,我可以进一步解释,但使用 mapDispatchToProps 提供的方法。

        4
  •  0
  •   Lelouch    7 年前

    正如上面的用户所说,最好考虑分离关注点。我会这样做:

    • 为每个范围创建一整套redux生命周期文件,例如:。 login .这包括行动、传奇和减速器。
    • 对于您案例中的每个范围 登录名 ,我会将其链接路由到 container 有权访问 global state 具有 connect() .只有该组件才有权访问dispatch,其子组件只能在dispatch中引用它,使其保持沉默并保持真相来源。

    注意:只要使用redux生命周期, 无论您从何处调用动作创建者 只要您的全球州 connect()

        5
  •  0
  •   CouchCode    7 年前

    您可以在应用程序中添加和更改以下内容。js公司

    import { bindActionCreators } from 'redux';
    import * as actionCreators from "../yourActionFile"
    
    class App extends React.Component {
    
       handleSubmit(e){
           e.preventDefault();
           this.props.updateUser(("yourArgumentsHere"))
       }
    
    
              render() {
                return (
                    <BrowserRouter>
                      <Root>
                        <Route exact path="/" component={Home}/>
                        <Route exact path="/login" component={Login}/>
                        <Route exact path="/register" component={Register}/>
                        <Route exact path='/books' component={Books}/>
                        <Route path='/books/:id' component={BookDetail}/>
                      </Root>
                    </BrowserRouter>
                );
              };
            }
    
            const mapStateToProps = (state) => {
              return {
                userData: state.saveUserData
              }
            };
    
            const mapDispatchToProps = (dispatch) => {
            return {
              updateUser: bindAtionCreators(actionCreators.updateUser, dispatch)
              //the logic for updating your store or "global state" should be done in the reducer
              }
            }
          };
    
    
          export default connect(mapStateToProps, mapDispatchToProps)(App)
    

    您可以将handleSubmit函数作为道具传递给子组件,也可以将mapDispatchToProps和handleSubmit添加到子组件。无论哪种方式,如果应用程序中的所有动态数据都依赖于应用商店,那么一旦reducer更新了应用商店,这些更改就会逐渐减少。

    推荐文章