我在屏幕上调用我的GET_REQUEST
动作,我的Saga
发出请求并触发我的GET_SUCCESS
动作,但是,只有在以下情况下,我的屏幕才会自行呈现我更新了组件的状态,触发了重新渲染,并显示出反映商店的新道具。
所以...我的商店显然运转良好,在Reactotron中商店已满,如下所示:
https://user-images.githubusercontent.com/27635248/54751638-52671b80-4bba-11e9-9f9b-b7eee7f3deba.png
我的user
减速器:
export const Types = {
GET_SUCCESS: 'user/GET_SUCCESS',
GET_REQUEST: 'user/GET_REQUEST',
GET_FAILURE: 'user/GET_FAILURE',
UPDATE_USER: 'user/UPDATE_USER',
REHYDRATE_SUCCESS: 'user/REHYDRATE_SUCCESS',
REHYDRATE_FAILURE: 'user/REHYDRATE_FAILURE',
}
const INITIAL_STATE = {
data: {},
status: {
loading: false,
error: null,
}
}
export default function user(state = INITIAL_STATE, action) {
switch (action.type) {
case Types.GET_SUCCESS: {
return {
...state,
data: action.payload.user,
status: {
...state.status,
loading: false,
error: null
}
}
}
case Types.GET_REQUEST: {
return {
...state,
status: {
error: null,
loading: true
}
}
}
case Types.GET_FAILURE: {
if (Object.keys(state.data).length) {
action.payload.isStateEmpty = false
return {
...state,
status: {
...state.status,
loading: false
}
}
} else {
return {
...state,
status: {
...state.status,
loading: false
}
}
}
}
case Types.REHYDRATE_SUCCESS: {
return {
...state,
data: action.payload.user,
status: {
error: null,
loading: false
}
}
}
case Types.REHYDRATE_FAILURE: {
return {
...state,
status: {
loading: false,
error: action.payload.error
}
}
}
case Types.UPDATE_USER: {
return {
...state,
data: {
...state.data,
...action.payload.data
}
}
}
default: {
return state
}
}
}
export const Creators = {
getUserSuccess: user => ({
type: Types.GET_SUCCESS,
payload: {
user,
},
}),
getUserRequest: () => ({
type: Types.GET_REQUEST,
payload: {}
}),
getUserFailure: error => ({
type: Types.GET_FAILURE,
payload: {
error,
isStateEmpty: true
}
}),
rehydrateUserSuccess: user => ({
type: Types.REHYDRATE_SUCCESS,
payload: {
user
}
}),
rehydrateUserFailure: error => ({
type: Types.REHYDRATE_FAILURE,
payload: {
error
}
}),
updateUser: data => ({
type: Types.UPDATE_USER,
payload: {
data
}
}),
}
我的user
传奇:
import { Types } from '../ducks/user'
import { call, put, takeLatest } from 'redux-saga/effects'
import { Creators as UserActions } from '../ducks/user'
import API from 'utils/api'
import DAO from '../../utils/dao';
function formatUsuarioToDbKeys(usuario, pk_pessoa) {
return {
pk_user: usuario.id,
fk_pessoa: pk_pessoa,
username_usu: usuario.username,
email_usu: usuario.email,
first_name_usu: usuario.first_name,
las_name_usu: usuario.last_name,
ativo_usu: usuario.ativo,
}
}
function formatPessoaToDbKeys(pessoa) {
return {
pk_pessoa: pessoa.pessoa_id,
fk_funcao: pessoa.funcao_id,
nome_pes: pessoa.nome,
codigo_erp_pes: pessoa.codigo_erp,
ativo_pes: pessoa.ativa,
}
}
function formatFuncaoToDbKeys(funcao) {
return {
pk_funcao: funcao.id,
nome_fun: funcao.nome,
}
}
function formatEquipeToDbKeys(equipe) {
return {
pk_equipe: equipe.id,
nome_equ: equipe.nome,
ativo_equ: equipe.ativo,
}
}
function* getUser() {
try {
const dados = yield call(API.getInstance().getRequest, '/initializer/?tipo=1')
const funcao = yield call(formatFuncaoToDbKeys, dados.funcao)
const pessoa = yield call(formatPessoaToDbKeys, dados.pessoa)
const usuario = yield call(formatUsuarioToDbKeys, ...[dados.usuario, dados.pessoa.pessoa_id])
const equipe = yield call(formatEquipeToDbKeys, dados.equipe)
yield put(UserActions.getUserSuccess({ usuario, pessoa, funcao, equipe }))
yield call(DAO.getInstance().createFuncao, funcao)
yield call(DAO.getInstance().createPessoa, pessoa)
yield call(DAO.getInstance().createUsuario, usuario)
yield call(DAO.getInstance().createEquipe, equipe)
} catch (error) {
yield put(UserActions.getUserFailure('Ocorreu um erro ao buscar dados do usuário.'))
yield call(console.warn, error.message)
}
}
function* rehydrateUser(action) {
if (action) {
try {
const user = yield call(DAO.getInstance().getUsuario)
yield put(UserActions.rehydrateUserSuccess(user))
} catch (error) {
yield put(UserActions.rehydrateUserFailure('Ocorreu um erro ao buscar dados do usuário.'))
yield call(console.warn, error.message)
}
}
}
export default sagas = [
takeLatest(Types.GET_REQUEST, getUser),
takeLatest(Types.GET_FAILURE, rehydrateUser)
]
最后,这是我的组件:
class Dashboard extends Component {
...state and others methods
componentDidMount() {
this.props.getUserRequest()
this.props.getConstructionsRequest()
}
render() {
const spin = this.state.spin_value.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
return (
<View style={{ flex: 1 }}>
<StatusBar
hidden={false}
backgroundColor={colors.blueDarker} />
<ScrollView
ref={'refScrollView'}
stickyHeaderIndices={[2]}
style={styles.container}>
<ImageBackground
source={require('imgs/capa_dashboard.png')}
blurRadius={4}
style={styles.imageBackground}>
<View style={styles.headerContainer}>
<Text style={styles.textVersion}>{require('../../../package.json').version}</Text>
<Animated.View style={{ transform: [{ rotate: spin }] }}>
<TouchableOpacity
onPress={() => {
this._spinIcon()
this._handleRefresh()
}}>
<MaterialIcons
name={'refresh'}
size={35}
style={styles.refreshIcon}
color={'white'} />
</TouchableOpacity>
</Animated.View>
</View>
<View style={styles.headerButtonsContainer}>
<HeaderLeft
navigation={this.props.navigation} />
<Image
style={styles.image}
source={require('imgs/logo_header.png')} />
<HeaderRight />
</View>
<View style={{ flex: 1, justifyContent: 'center' }}>
{!this.props.user.status.loading && !!Object.keys(this.props.user.data).length
? <View>
<Text style={[styles.textNome, { textAlign: 'center' }]}>{this.props.user.data.pessoa.nome_pes}</Text>
<Text style={styles.textAuxiliar}>{this.props.user.data.funcao.pk_funcao == 1 ? this.props.user.data.equipe.nome_equ : 'Engenheiro(a)'}</Text>
</View>
: <ActivityIndicator style={{ alignSelf: 'center' }} size={'large'} color={colors.blue} />
}
</View>
</ImageBackground>
<ActionsButtons
stylesButton1={{ borderBottomColor: this.state.button_obras_selected ? 'white' : colors.transparent }}
stylesText1={{ color: this.state.button_obras_selected ? 'white' : 'lightgrey' }}
numberButton1={this.props.count_obras}
callbackButton1={async () => this.setState({ button_obras_selected: true })}
stylesButton2={{ borderBottomColor: !this.state.button_obras_selected ? 'white' : colors.transparent }}
stylesText2={{ color: !this.state.button_obras_selected ? 'white' : 'lightgrey' }}
numberButton2={this.state.count_enviar}
callbackButton2={async () => this.setState({ button_obras_selected: false })}
/>
<View>
{this.state.button_obras_selected
? <View style={styles.containerTextInput} >
<TextInput
onEndEditing={() => this._handleEndSearch()}
onFocus={() => this._handleStartSearch()}
ref={'textInputRef'}
style={styles.textInput}
onChangeText={search_text => this._filterSearch(search_text)}
autoCapitalize="none"
onSubmitEditing={() => this._handleClickSearch()}
underlineColorAndroid='rgba(255,255,255,0.0)'
placeholder={'Buscar obras...'} />
<TouchableOpacity
onPress={() => this._handleClickSearch()}
style={styles.searchImage}>
<MaterialCommunityIcons
name={'magnify'}
size={30}
color={'black'} />
</TouchableOpacity>
</View >
: null}
</View>
<View>
{this.state.button_obras_selected
? !this.props.constructions.status.loading && !!Object.keys(this.props.constructions.data).length
? <View style={{ flex: 1, marginTop: 8 }}>
<FlatList
data={this.props.constructions.data}
removeClippedSubviews={true}
keyExtractor={item => item.pk_obra.toString()}
renderItem={obra =>
<ObraComponent
callback={this._callbackObra}
item={obra.item}
isCurrentObra={nome => this.state.obra_atual == nome}
/>
}
/>
</View>
: <ActivityIndicator style={{ marginTop: 150 }} size={50} color={colors.blueDarker} />
:
<View style={{
flex: 1,
marginTop: 8,
}}>
<FlatList
data={[]}
removeClippedSubviews={true}
keyExtractor={item => item.fk_obra.toString()}
renderItem={acao =>
<AcaoComponent item={acao.item} />
}
/>
</View>
}
</View>
</ScrollView>
</View>
)
}
}
const mapStateToProps = state => ({
user: state.user,
constructions: state.constructions,
count_obras: state.constructions.data.length
})
const mapDispatchToProps = dispatch =>
bindActionCreators({
...UserActions,
...ConstructionsActions.DEEP,
...ConstructionsActions.SHALLOW
}, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard)
正如我之前说的,我调用getUserRequest()
在GET_REQUEST
上执行componentDidMount()
操作,但是,当请求完成并且我的redux存储已更新时,我的屏幕仍然像这样加载,即使商店中的加载状态为false
https://user-images.githubusercontent.com/27635248/54752551-4e88c880-4bbd-11e9-9164-5fad46db6c13.png
我在其他项目中使用了非常相似的store和saga结构,但由于某种原因,在这一特定项目中,我无法弄清楚
当我按下这些按钮之一时,组件会更新其状态并触发重新渲染,然后最终我商店中的数据会在检查区域显示为道具-这是我想出的唯一方法-使用redux渲染东西(至少在此项目中如此),通过使用this.setState()
更新组件状态来强制重新渲染:
https://user-images.githubusercontent.com/27635248/54752771-fef6cc80-4bbd-11e9-9d15-a46acb87f3a4.png
我不确定自己的实现方式有什么问题,我已经检查了其他类似的问题,这些问题似乎都不起作用,如果有人有建议,欢迎在下面发布,谢谢您。
"axios": "^0.18.0",
"react-native": "~0.55.2",
"react-redux": "^6.0.0",
"reactotron-redux": "^2.1.3",
"reactotron-redux-saga": "^4.0.1",
"redux": "^4.0.1",
"redux-saga": "^1.0.1",
编辑:
因此,据我所知,更新redux的商店反映了已连接组件的新道具,而React会根据浅表比较来更新它们。我做了一些实验,由于某种原因,我的动作被触发,sagas运行正常,并且商店正在成功更改,但是触发我的成功动作后未调用mapStateToProps
。
基本上,我无法选择是否更新组件(使用shouldComponentUpdate
比较prevProps
和this.props
),因为该组件甚至没有从mapStateToProps
接收新道具。
答案 0 :(得分:0)
您的代码没有错。这正是它的行为方式。
当redux状态更改时,您的react组件不应重新渲染。更改redux状态只会更改道具。您可以根据新道具决定是否要重新渲染组件,这可以通过更改组件的状态来完成。
如果您想查看新道具是否正在显示,则应使用componentDidUpdate(prevProps)
lifeCycle方法,而不要使用componentDidMount()
。您可以按照以下步骤进行操作:
componentDidUpdate(prevProps) {
if (prevProps.someProp != this.props.someProp) {
console.log(this.props);
}
}