使用带有 react-navigation 的 redux store
Using a redux store with react-navigation
我正在尝试使用 expo 将 redux 存储添加到我的 react-native 应用程序。
应用程序如下所示:
import React from 'react';
// Persistent storage
import AsyncStorage from '@react-native-async-storage/async-storage';
// Navigation
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Login from './Login';
import Home from './Home';
import BoardScreen from './BoardScreen';
import BoardDetailsScreen from './BoardDetailsScreen';
import CardDetailsScreen from './CardDetailsScreen';
// Store
import { Provider } from 'react-redux';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import store from './store/store';
import { setNCServer, setToken } from './store/actions';
// For creating an URL handler to retrieve the device token
import * as Linking from 'expo-linking';
// Create Stack navigator
const Stack = createStackNavigator()
// Application
class App extends React.Component {
constructor(props) {
super(props)
// Retrieve token from storage if available
AsyncStorage.getItem('token').then(token => {
this.props.state.setToken(token)
})
// Register handler to catch Nextcloud's redirect after successfull login
Linking.addEventListener('url', (url) => {this.handleRedirect(url)})
}
// Function to retrieve the device's token and save it after user logged in
handleRedirect = async (url) => {
if (url.url.startsWith('nc://login/server')) {
try {
token = url.url.substring(url.url.lastIndexOf(':'))
console.log('Persisting token', token)
AsyncStorage.setItem('token', token);
this.props.state.setToken(token)
} catch (e) {
// TODO
}
}
}
render() {
if (this.state.token === null) {
// No token is stored yet, we need to get one
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
} else {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="AllBoard" component={BoardScreen} options={{title: 'All boards'}} />
<Stack.Screen name="BoardDetails" component={BoardDetailsScreen} options={{title: 'Board details'}} />
<Stack.Screen name="CardDetails" component={CardDetailsScreen} options={{title: 'Card details'}} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
}
}
}
// Initialise store
const mapDispatchToProps = dispatch => (
bindActionCreators({
setNCServer,
setToken,
}, dispatch)
);
const mapStateToProps = (state) => {
return state
};
const connector = connect(mapStateToProps, mapDispatchToProps)
export default connector(App);
问题:当我启动应用程序时,我收到臭名昭著的错误“错误:无法在“Connect(App)”的上下文中找到“store”。
我已经为这个问题绞尽脑汁好几个小时了,但找不到我做错了什么。
谁能帮帮我?
您只能在 Redux Provider
组件的 内部 组件上使用 connect
。现在你的 Provider
在 App
里面,所以你不能在 App
上使用 connect
。您需要将 Provider
上移一个级别。
我在这里也看到了很多其他问题:this.props.state.setToken(token)
应该是 this.props.setToken(token)
并且 this.state.token
应该是 this.props.state.token
-- 但实际上你应该是使用 mapStateToProps
只是 select 令牌而不是 return 整个状态!
您可能会发现 useSelector
和 useDispatch
挂钩更直观。
url.url
这个名字很容易混淆。实际上,这是一个带有 属性 url
的事件,因此您应该将其命名为 e
或 event
。但您也可以将其解构为 ({url})
而不是 (e)
和 e.url
.
// Application contents
class AppNavigation extends React.Component {
constructor(props) {
super(props);
// Retrieve token from storage if available
AsyncStorage.getItem('token').then((token) => {
this.props.setToken(token);
});
// Register handler to catch Nextcloud's redirect after successfull login
Linking.addEventListener('url', this.handleRedirect);
}
// Function to retrieve the device's token and save it after user logged in
handleRedirect = async ({ url }) => {
if (url.startsWith('nc://login/server')) {
try {
const token = url.substring(url.lastIndexOf(':'));
console.log('Persisting token', token);
AsyncStorage.setItem('token', token);
this.props.setToken(token);
} catch (e) {
// TODO
}
}
};
render() {
if (this.props.token === null) {
// No token is stored yet, we need to get one
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
} else {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="AllBoard"
component={BoardScreen}
options={{ title: 'All boards' }}
/>
<Stack.Screen
name="BoardDetails"
component={BoardDetailsScreen}
options={{ title: 'Board details' }}
/>
<Stack.Screen
name="CardDetails"
component={CardDetailsScreen}
options={{ title: 'Card details' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
}
// Can just be an object of functions to bind - creates props `setNCServer` and `setToken`
const mapDispatchToProps = {
setNCServer,
setToken,
};
// Creates a prop `token` with the token from state
const mapStateToProps = (state) => {
return {
token: state.token,
};
};
const connector = connect(mapStateToProps, mapDispatchToProps);
const ConnectedNavigation = connector(AppNavigation);
// use the connected component inside a redux Provider
export default function App() {
return (
<Provider store={store}>
<ConnectedNavigation />
</Provider>
);
}
我正在尝试使用 expo 将 redux 存储添加到我的 react-native 应用程序。
应用程序如下所示:
import React from 'react';
// Persistent storage
import AsyncStorage from '@react-native-async-storage/async-storage';
// Navigation
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import Login from './Login';
import Home from './Home';
import BoardScreen from './BoardScreen';
import BoardDetailsScreen from './BoardDetailsScreen';
import CardDetailsScreen from './CardDetailsScreen';
// Store
import { Provider } from 'react-redux';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import store from './store/store';
import { setNCServer, setToken } from './store/actions';
// For creating an URL handler to retrieve the device token
import * as Linking from 'expo-linking';
// Create Stack navigator
const Stack = createStackNavigator()
// Application
class App extends React.Component {
constructor(props) {
super(props)
// Retrieve token from storage if available
AsyncStorage.getItem('token').then(token => {
this.props.state.setToken(token)
})
// Register handler to catch Nextcloud's redirect after successfull login
Linking.addEventListener('url', (url) => {this.handleRedirect(url)})
}
// Function to retrieve the device's token and save it after user logged in
handleRedirect = async (url) => {
if (url.url.startsWith('nc://login/server')) {
try {
token = url.url.substring(url.url.lastIndexOf(':'))
console.log('Persisting token', token)
AsyncStorage.setItem('token', token);
this.props.state.setToken(token)
} catch (e) {
// TODO
}
}
}
render() {
if (this.state.token === null) {
// No token is stored yet, we need to get one
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
} else {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="AllBoard" component={BoardScreen} options={{title: 'All boards'}} />
<Stack.Screen name="BoardDetails" component={BoardDetailsScreen} options={{title: 'Board details'}} />
<Stack.Screen name="CardDetails" component={CardDetailsScreen} options={{title: 'Card details'}} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
}
}
}
// Initialise store
const mapDispatchToProps = dispatch => (
bindActionCreators({
setNCServer,
setToken,
}, dispatch)
);
const mapStateToProps = (state) => {
return state
};
const connector = connect(mapStateToProps, mapDispatchToProps)
export default connector(App);
问题:当我启动应用程序时,我收到臭名昭著的错误“错误:无法在“Connect(App)”的上下文中找到“store”。
我已经为这个问题绞尽脑汁好几个小时了,但找不到我做错了什么。
谁能帮帮我?
您只能在 Redux Provider
组件的 内部 组件上使用 connect
。现在你的 Provider
在 App
里面,所以你不能在 App
上使用 connect
。您需要将 Provider
上移一个级别。
我在这里也看到了很多其他问题:this.props.state.setToken(token)
应该是 this.props.setToken(token)
并且 this.state.token
应该是 this.props.state.token
-- 但实际上你应该是使用 mapStateToProps
只是 select 令牌而不是 return 整个状态!
您可能会发现 useSelector
和 useDispatch
挂钩更直观。
url.url
这个名字很容易混淆。实际上,这是一个带有 属性 url
的事件,因此您应该将其命名为 e
或 event
。但您也可以将其解构为 ({url})
而不是 (e)
和 e.url
.
// Application contents
class AppNavigation extends React.Component {
constructor(props) {
super(props);
// Retrieve token from storage if available
AsyncStorage.getItem('token').then((token) => {
this.props.setToken(token);
});
// Register handler to catch Nextcloud's redirect after successfull login
Linking.addEventListener('url', this.handleRedirect);
}
// Function to retrieve the device's token and save it after user logged in
handleRedirect = async ({ url }) => {
if (url.startsWith('nc://login/server')) {
try {
const token = url.substring(url.lastIndexOf(':'));
console.log('Persisting token', token);
AsyncStorage.setItem('token', token);
this.props.setToken(token);
} catch (e) {
// TODO
}
}
};
render() {
if (this.props.token === null) {
// No token is stored yet, we need to get one
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
} else {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="AllBoard"
component={BoardScreen}
options={{ title: 'All boards' }}
/>
<Stack.Screen
name="BoardDetails"
component={BoardDetailsScreen}
options={{ title: 'Board details' }}
/>
<Stack.Screen
name="CardDetails"
component={CardDetailsScreen}
options={{ title: 'Card details' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
}
// Can just be an object of functions to bind - creates props `setNCServer` and `setToken`
const mapDispatchToProps = {
setNCServer,
setToken,
};
// Creates a prop `token` with the token from state
const mapStateToProps = (state) => {
return {
token: state.token,
};
};
const connector = connect(mapStateToProps, mapDispatchToProps);
const ConnectedNavigation = connector(AppNavigation);
// use the connected component inside a redux Provider
export default function App() {
return (
<Provider store={store}>
<ConnectedNavigation />
</Provider>
);
}