React / Redux-앱로드 / 초기화에 대한 작업 전달
서버에서 토큰 인증을 받았으므로 Redux 앱이 처음로드 될 때 사용자가 인증되었는지 여부를 확인하기 위해이 서버에 요청을해야하며, 그렇다면 토큰을 받아야합니다.
Redux 코어 INIT 작업을 사용하는 것이 권장되지 않는 것으로 나타났습니다. 앱이 렌더링되기 전에 어떻게 작업을 전달할 수 있습니까?
루트 componentDidMount
방식으로 액션을 전달할 render
수 있으며 방식으로 인증 상태를 확인할 수 있습니다.
이 같은:
class App extends Component {
componentDidMount() {
this.props.getAuth()
}
render() {
return this.props.isReady
? <div> ready </div>
: <div>not ready</div>
}
}
const mapStateToProps = (state) => ({
isReady: state.isReady,
})
const mapDispatchToProps = {
getAuth,
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
나는 이것을 위해 제시된 해결책에 만족하지 않았고, 렌더링이 필요한 클래스에 대해 생각하고 있다는 생각이 들었습니다. 방금 시작을위한 클래스를 만든 다음 componentDidMount
메서드에 항목을 푸시 하고 render
디스플레이에 로딩 화면 만 표시하면 어떨까요?
<Provider store={store}>
<Startup>
<Router>
<Switch>
<Route exact path='/' component={Homepage} />
</Switch>
</Router>
</Startup>
</Provider>
그리고 다음과 같은 것이 있습니다.
class Startup extends Component {
static propTypes = {
connection: PropTypes.object
}
componentDidMount() {
this.props.actions.initialiseConnection();
}
render() {
return this.props.connection
? this.props.children
: (<p>Loading...</p>);
}
}
function mapStateToProps(state) {
return {
connection: state.connection
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Startup);
그런 다음 몇 가지 redux 작업을 작성하여 앱을 비동기 적으로 초기화합니다. 치료 효과가 있습니다.
업데이트 :이 답변은 React Router 3입니다.
나는 react-router onEnter props를 사용하여이 문제를 해결했습니다 . 코드는 다음과 같습니다.
// this function is called only once, before application initially starts to render react-route and any of its related DOM elements
// it can be used to add init config settings to the application
function onAppInit(dispatch) {
return (nextState, replace, callback) => {
dispatch(performTokenRequest())
.then(() => {
// callback is like a "next" function, app initialization is stopped until it is called.
callback();
});
};
}
const App = () => (
<Provider store={store}>
<IntlProvider locale={language} messages={messages}>
<div>
<Router history={history}>
<Route path="/" component={MainLayout} onEnter={onAppInit(store.dispatch)}>
<IndexRoute component={HomePage} />
<Route path="about" component={AboutPage} />
</Route>
</Router>
</div>
</IntlProvider>
</Provider>
);
여기에있는 모든 대답은 루트 구성 요소를 만들고 componentDidMount에서 실행하는 변형 인 것 같습니다. 내가 redux에 대해 가장 좋아하는 것 중 하나는 구성 요소 수명주기에서 데이터 가져 오기를 분리한다는 것입니다. 이 경우에 다른 이유가 없습니다.
스토어를 루트 index.js
파일 로 가져 오는 경우 해당 파일에서 액션 생성자 (라고 부르 자 initScript()
)를 디스패치하면 무엇이든로드되기 전에 실행됩니다.
예를 들면 :
//index.js
store.dispatch(initScript());
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.getElementById('root')
);
React Hooks를 사용하는 경우 한 줄 솔루션은 다음과 같습니다.
useEffect(() => store.dispatch(handleAppInit()), []);
빈 배열은 첫 번째 렌더링에서 한 번만 호출되도록합니다.
전체 예 :
import React, { useEffect } from 'react';
import { Provider } from 'react-redux';
import AppInitActions from './store/actions/appInit';
function App() {
useEffect(() => store.dispatch(AppInitActions.handleAppInit()), []);
return (
<Provider store={store}>
<div>
Hello World
</div>
</Provider>
);
}
export default App;
유사하지만 위의 대안 (React-Router v3에서만 작동하는 것으로 보입니다) :
Routes.js
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from '../components/App';
import Home from '../views/Home';
import OnLoadAuth from '../containers/app/OnLoadAuth';
export default = (
<Route path="/" component={OnLoadAuth(App)}>
<IndexRoute component={Home} />
{* Routes that require authentication *}
</Route>
);
OnLoadAuth.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { authenticateUser, fetchingUser } from '../../actions/AuthActionCreators';
import Spinner from '../../components/loaders/Spinner';
export default App => {
class OnLoadAuth extends Component {
componentDidMount = () => this.props.authenticateUser();
render = () => (
(this.props.isLoading === undefined || this.props.isLoading)
? <Spinner />
: <App {...this.props} />
)
}
return connect(
state => ({ isLoading: state.auth.fetchingUser }),
{ authenticateUser, fetchingUser }
)(OnLoadAuth);
};
으로 REDUX-사가 당신이 멋지게 그것을 할 수있는 미들웨어.
트리거되기 전에 디스패치 된 작업 (예 : take
또는 takeLatest
)을 감시하지 않는 사가를 정의하십시오 . 그런 식으로 fork
루트 사가에서 ed하면 앱 시작시 정확히 한 번 실행됩니다.
다음은 redux-saga
패키지 에 대한 약간의 지식이 필요 하지만 요점을 설명 하는 불완전한 예입니다 .
sagas / launchSaga.js
import { call, put } from 'redux-saga/effects';
import { launchStart, launchComplete } from '../actions/launch';
import { authenticationSuccess } from '../actions/authentication';
import { getAuthData } from '../utils/authentication';
// ... imports of other actions/functions etc..
/**
* Place for initial configurations to run once when the app starts.
*/
const launchSaga = function* launchSaga() {
yield put(launchStart());
// Your authentication handling can go here.
const authData = yield call(getAuthData, { params: ... });
// ... some more authentication logic
yield put(authenticationSuccess(authData)); // dispatch an action to notify the redux store of your authentication result
yield put(launchComplete());
};
export default [launchSaga];
위의 코드 는 생성해야하는 launchStart
및 launchComplete
redux 액션을 전달합니다 . 실행이 시작되거나 완료 될 때마다 다른 작업을 수행하도록 상태에 알리는 데 유용한 작업을 만드는 것이 좋습니다.
루트 사가는이 launchSaga
사가 를 포크해야합니다 .
sagas / index.js
import { fork, all } from 'redux-saga/effects';
import launchSaga from './launchSaga';
// ... other saga imports
// Single entry point to start all sagas at once
const root = function* rootSaga() {
yield all([
fork( ... )
// ... other sagas
fork(launchSaga)
]);
};
export default root;
이에 대한 자세한 정보 는 redux-saga 의 정말 좋은 문서를 읽으 십시오.
다음은 최신 React (16.8) Hooks를 사용한 답변입니다.
import { appPreInit } from '../store/actions';
// app preInit is an action: const appPreInit = () => ({ type: APP_PRE_INIT })
import { useDispatch } from 'react-redux';
export default App() {
const dispatch = useDispatch();
// only change the dispatch effect when dispatch has changed, which should be never
useEffect(() => dispatch(appPreInit()), [ dispatch ]);
return (<div>---your app here---</div>);
}
사용 : Apollo Client 2.0, React-Router v4, React 16 (Fiber)
선택한 답변은 이전 React Router v3을 사용합니다. 앱의 전역 설정을로드하려면 '디스패치'를해야했습니다. 트릭은 componentWillUpdate를 사용하는 것입니다. 예제에서는 아폴로 클라이언트를 사용하고 있으며 솔루션을 가져 오지 않는 것은 동일합니다. 당신은 boucle의 필요하지 않습니다
SettingsLoad.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {bindActionCreators} from "redux";
import {
graphql,
compose,
} from 'react-apollo';
import {appSettingsLoad} from './actions/appActions';
import defQls from './defQls';
import {resolvePathObj} from "./utils/helper";
class SettingsLoad extends Component {
constructor(props) {
super(props);
}
componentWillMount() { // this give infinite loop or no sense if componente will mount or not, because render is called a lot of times
}
//componentWillReceiveProps(newProps) { // this give infinite loop
componentWillUpdate(newProps) {
const newrecord = resolvePathObj(newProps, 'getOrgSettings.getOrgSettings.record');
const oldrecord = resolvePathObj(this.props, 'getOrgSettings.getOrgSettings.record');
if (newrecord === oldrecord) {
// when oldrecord (undefined) !== newrecord (string), means ql is loaded, and this will happens
// one time, rest of time:
// oldrecord (undefined) == newrecord (undefined) // nothing loaded
// oldrecord (string) == newrecord (string) // ql loaded and present in props
return false;
}
if (typeof newrecord ==='undefined') {
return false;
}
// here will executed one time
setTimeout(() => {
this.props.appSettingsLoad( JSON.parse(this.props.getOrgSettings.getOrgSettings.record));
}, 1000);
}
componentDidMount() {
//console.log('did mount this props', this.props);
}
render() {
const record = resolvePathObj(this.props, 'getOrgSettings.getOrgSettings.record');
return record
? this.props.children
: (<p>...</p>);
}
}
const withGraphql = compose(
graphql(defQls.loadTable, {
name: 'loadTable',
options: props => {
const optionsValues = { };
optionsValues.fetchPolicy = 'network-only';
return optionsValues ;
},
}),
)(SettingsLoad);
const mapStateToProps = (state, ownProps) => {
return {
myState: state,
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators ({appSettingsLoad, dispatch }, dispatch ); // to set this.props.dispatch
};
const ComponentFull = connect(
mapStateToProps ,
mapDispatchToProps,
)(withGraphql);
export default ComponentFull;
App.js
class App extends Component<Props> {
render() {
return (
<ApolloProvider client={client}>
<Provider store={store} >
<SettingsLoad>
<BrowserRouter>
<Switch>
<LayoutContainer
t={t}
i18n={i18n}
path="/myaccount"
component={MyAccount}
title="form.myAccount"
/>
<LayoutContainer
t={t}
i18n={i18n}
path="/dashboard"
component={Dashboard}
title="menu.dashboard"
/>
참고URL : https://stackoverflow.com/questions/38563679/react-redux-dispatch-action-on-app-load-init
'Program Tip' 카테고리의 다른 글
curl을 사용하여 Chrome을 사용하는 것과 똑같은 GET 요청을 얻는 방법은 무엇입니까? (0) | 2020.11.24 |
---|---|
운영 체제 또는 아키텍처와 호환되지 않음 : fsevents@1.0.11 (0) | 2020.11.24 |
Python을 사용하여 두 개의 플롯을 나란히 만드는 방법은 무엇입니까? (0) | 2020.11.24 |
ArrayList의 차이점 (0) | 2020.11.24 |
특정 열에서 NaN으로 행을 선택하는 방법은 무엇입니까? (0) | 2020.11.24 |