Как вызвать диспетчерское действие от дочернего компонента в реагировать? - PullRequest
0 голосов
/ 10 мая 2018

У меня есть компонент App, компонент Login.На моей странице входа в систему, когда пользователь успешно передает свои учетные данные, API возвращает данные пользователя в качестве ответа.Я хочу сохранить эти пользовательские данные как глобальное состояние.Вот почему я использовал redux.

My index.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'));

В моем 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)

В моем Login.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}
        });
      }
    });
  }

В компоненте входа в систему я хочу вызвать метод диспетчера компонента App updateUser с this.props.updateUser(response.user);.Но это не работает.

У кого-нибудь есть идеи, как мне этого добиться?Вы можете увидеть полный код в github

Ответы [ 5 ]

0 голосов
/ 11 мая 2018

Сначала несколько вещей.

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);

EDIT:

Проверил репозиторий на github. Я думаю, что первая проблема заключается в импорте Login компонента как {Login} в App.js, который должен быть import Login вместо этого, поскольку он экспортируется по умолчанию из Login.js

Второе - использование компонента в атрибуте рендеринга Route, как показано ниже. <Route exact path="/login" render={() => (<Login/>)}/>

Это решило проблему для меня, поэтому должно сделать то же самое для вас.

0 голосов
/ 10 мая 2018

вы можете добавить и изменить следующее в app.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 в дочерний компонент.В любом случае, если вы полагаетесь на хранилище для всех динамических данных в вашем приложении, изменения будут снижаться после обновления хранилища редуктором.

0 голосов
/ 10 мая 2018

Вот как бы я это сделал. Прежде всего, вам не нужно подключать ваш app.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;
  } 
};

Создайте отдельный файл действий и переместите запрос на выборку в файле actions.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);
0 голосов
/ 10 мая 2018

Как говорит пользователь выше, лучше рассмотреть разделение интересов.Вот что я хотел бы сделать:

  • создать полный набор файлов жизненного цикла для каждой области, например login.Это включает в себя действия, саги и редукторы.
  • для каждой области в вашем случае login, я бы направил его ссылку на container, который имеет доступ к global state с connect().Один только этот компонент имеет доступ к диспетчеризации, и его дочерние компоненты должны ссылаться на него только для диспетчеров, сохраняя их тупость и сохраняя источник правды.* не имеет значения, куда вы звоните создателю действия с он будет доступен вашему глобальному состоянию, если он имеет connect()
0 голосов
/ 10 мая 2018

В идеале вы должны хранить ваш метод updateUser в файле действий и импортировать этот метод там, где вам нужно его использовать.

actions.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);
};

Когда вы обнаружите, что у вас есть целый ряд действий, которые вы, вероятно, будете выполнять, когда ваше приложение расширяется в размере и сложности, этоподход поможет вам правильно управлять своими действиями.

Всегда учитывайте Разделение проблем - цель вашего actions.js файла состоит в том, чтобы содержать все ваши действия, ничего больше, ион гораздо лучше расположен здесь, чем в файле app.js .Однако, кроме того, я лично имел бы папку actions и содержал бы этот метод в файле user.js в этом каталоге.

Надеюсь, это поможет.

Редактировать

Я добавил использование bindActionCreators в mapDispatchToProps.Извините, прошло много времени с тех пор, как я сделал это таким образом - я лично экспортирую store.dispatch из моего app.js и импортирую и использую его там, где мне нужно, вместо того, чтобы использовать mapDispatchToProps.Я могу объяснить, если хотите, но на самом деле хорошей практикой является использование предоставленного метода mapDispatchToProps.

...