I can't use default state value in redux
Hello,
I have a function to dispatch Login action,
I want in the initial to be false,
but when I put this value in the render method, it's given me an error
can't read property 'isLogin' of undefined
although in the reducer i add an initialState
let initialState = {
isLogin: false,
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case IS_LOGIN:
state = {
...state,
isLogin: true,
};
break;
default:
return state;
}
return state;
};
here's Store
const store = createStore(
combineReducers({
user: userReducer,
count: countPlayReducer,
}),
);
Action
export const isLoginFunc = isLogin => {
return {
type: IS_LOGIN,
payload: isLogin,
};
};
UI/Dispatching
import {isLoginFunc} from '../../redux/actions/isLoginAction';
signIn = async data => {
this.setState({loading: true}); // maybe here the wrong?
API.post('/login', data)
.then(async response => {
let {
data: {
data: {
response: {token},
},
},
} = response;
this.props.dispatch(isLoginFunc(true));
await saveToken('id_token', token);
this.setState({loading: false}, () =>
this.props.navigation.navigate({
key: 'TabHome',
routeName: 'TabHome',
}),
);
})
.catch(error => {
console.log(error);
this.setState({loading: false, error: error.response.data});
});
};
render(){
...
<Text>{this.props.user.isLogin}</Text>
...
}
mapStateToProps = state => {
return {
isLogin: state.user,
};
};
export default connect(mapStateToProps)(Login);
It looks like you're mapping redux state using the following:
mapStateToProps = state => {
return {
isLogin: state.user,
};
};
But then you're trying to access isLogin via:
this.props.user.isLogin
Looks like this should be changed to:
this.props.isLogin.isLogin
However you might want to alter your mapping to the following instead:
mapStateToProps = state => {
return {
isLogin: state.user.isLogin,
};
};
So that you can simply use:
this.props.isLogin
Related
The component does not re render after successfully update state in redux
i have tried to do some condition in componentShouldUpdate end up with loading true without change
reducer.js
import * as types from "./actionsType";
const INITIAL_STATE = {
slide_data: [],
error: null,
loading: false,
};
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties,
};
};
const slideReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.SLIDES_FETCH_START:
return updateObject(state, {
error: null,
loading: true,
});
case types.SLIDES_FETCH_SUCSSES:
return updateObject(state, {
slide_data: action.payload,
error: null,
loading: false,
});
case types.SLIDES_FETCH_FAIL:
return updateObject(state, {
error: action.error,
loading: false,
});
default:
return state;
}
};
export default slideReducer;
actions.js
import * as types from "./actionsType";
import axios from "axios";
import { selectSlides } from "./slides.selectors";
export const slidesStart = () => {
return {
type: types.SLIDES_FETCH_START,
};
};
export const slidesSucces = (slides) => {
return {
type: types.SLIDES_FETCH_SUCSSES,
payload: slides,
};
};
export const slidesFail = (error) => {
return {
type: types.SLIDES_FETCH_FAIL,
error: error,
};
};
export const fetchSlides = () => {
return (dispatch) => {
console.log("fetch Start");
dispatch(slidesStart());
axios
.get("http://127.0.0.1:8000/slides/", {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
dispatch(slidesSucces(res.data));
})
.catch((err) => dispatch(slidesFail(err)));
};
};
component
class IntroPage extends Component {
constructor(props) {
super(props);
this.tlitRef = React.createRef();
this.titlelRef = React.createRef();
this.subTitleRef = React.createRef();
this.showcase = React.createRef();
}
componentDidMount() {
this.props.fetchSlides();
}
render() {
const { slides, loading } = this.props;
if (loading) {
return <h1>Loading</h1>;
}
return (
<div className="intro">
<div className="wrapper">
{slides.map((data) => (
<SwiperSlides data={data} key={data.name} />
))}
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.slides.loading,
error: state.slides.error,
slides: state.slides.slide_data,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchSlides: () => dispatch(fetchSlides()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(IntroPage);
Register the redux-logger correctly. The data was returned but nothing changes when I do redux-persist and try to reload data come through
update ::
when I change the size of the browser data it correctly appears what is this !!
update : this problem related to swiperjs
and the solution will be like that:
1 - assign swiper instance to React.CreateRef(null) : this.swiper = React.CreateRef(null)
2 - in componentDidUpdate() make a swiper update : this.swiper.current.update()
4 - use a arrow function syntax in swiper on functions to refer to the outer scope
Im new to react, now Im creating a simple app using redux and redux-thunk which calls an API asynchronously. Here is my game gameAction:
export const fetchGamesStartAsync = () => {
return dispatch => {
dispatch(fetchGamesStart());
axiosGenCfg.post('/game/get',{
"page" : 1,
"size" : 10
})
.then(({ res }) => {
dispatch(fetchGamesSuccess(res));
})
.catch(err => {
dispatch(fetchGamesFailure(err.message));
});
}
};
const fetchGamesStart = () => ({
type: gameActionTypes.FETCH_GAMES_START,
});
const fetchGamesFailure = () => ({
type: gameActionTypes.FETCH_GAMES_FAILURE,
});
const fetchGamesSuccess = (games) => ({
type: gameActionTypes.FETCH_GAMES_SUCCESS,
payload:{
...games
}
});
and this is my gameReducer:
const INITIAL_STATE= {
gamesList : null,
isFetching: false,
errorMessage : undefined
};
const gameReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case gameActionTypes.FETCH_GAMES_START:
return{
...state,
isFetching: true
};
case gameActionTypes.FETCH_GAMES_SUCCESS:
return{
...state,
isFetching: false,
gamesList: action.payload
};
case gameActionTypes.FETCH_GAMES_FAILURE:
return{
...state,
isFetching: false,
errorMessage: action.payload
};
default:
return {
state
};
}
};
and in rootReducer
export default combineReducers({
admin : adminReducer,
game: gameReducer,
})
I also added redux-logger to check state and this is what i get in console
So why there are 2 levels of state in my game object? and also the same with admin object. before i add redux-thunk to project, I didn't have this problem. before adding redux-thunk currentAdmin was direct child of admin. But now there is a state object between.
default:
return {
state
};
should just be
default:
return state
Right now any time you hit the default, state.whatever is becoming state.state.whatever
I'm having trouble fetching a list of users from an api. I think issue might be in my mapDispatchToProps function but I'm not sure. Everything else seems fine to me. I'm new to redux and I'm kinda having a hard time wrapping my head around it so any help is appreciated
The list with the users would ideally be displayed as soon as the component mounts. I did the same thing without redux store and it was working just fine, I'm just not really sure how to integrate redux
Actions
export const startLoading = () => {
return {
type: START_LOADING
}
}
export const updateUserData = payload => {
return {
type: UPDATE_USER_DATA,
payload
}
}
export const updateUserError = payload => {
return {
type: UPDATE_USER_ERROR,
payload: payload
}
}
export function fetchUsers() {
return dispatch => {
dispatch(startLoading());
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
data = data.filter(user => user.id < 4);
data.forEach(user => {
user.isGoldClient = false;
user.salary = '4000';
user.photo = userThumbnail;
})
.then(data => {
dispatch(updateUserData(data));
}).catch(error => {
dispatch(updateUserError(error));
})
});
};
};
Reducers
const initialState = {
loading: false,
users: [],
error: null
};
export function userReducer(state=initialState, action){
switch(action.type){
case START_LOADING:
return {
...state,
loading: true
}
case UPDATE_USER_DATA:
return {
...state,
loading: false,
users: action.payload,
error: null
}
case UPDATE_USER_ERROR:
return {
...state,
error: action.payload,
loading: false,
users: []
};
default:
return state;
};
};
Component
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
usersAreDisplayed: true
};
}
componentDidMount() {
fetchUsers();
}
render(){
return (
<UserList users={this.state.users} />
)
}
}
function mapStateToProps(state){
return { users: state.users }
}
function mapDispatchToProps(dispatch){
return {
fetchUsers: payload => dispatch(updateUserData(payload)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Looks like you are not calling the actual fetchUsers at all.
Change the component code like this
function mapStateToProps(state){
return { users: state.users }
}
// remove this function
// function mapDispatchToProps(dispatch){
// return {
// fetchUsers: payload => dispatch(updateUserData(payload)),
// }
// }
export default connect(mapStateToProps, {fetchUsers})(Home); //<---- destructure it here. Also import the function (action)
1a. fetchUsers function needs to be accessed using this.props
componentDidMount() {
this.props.fetchUsers();
}
There is an extra then block after forEach.
Remove it.
export function fetchUsers() {
return (dispatch) => {
dispatch(startLoading());
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => {
data = data.filter((user) => user.id < 4);
data.forEach((user) => {
user.isGoldClient = false;
user.salary = "4000";
user.photo = userThumbnail;
});
dispatch(updateUserData(data)); // <------ no extra .then is required
})
.catch((error) => {
dispatch(updateUserError(error));
});
};
}
Also <UserList users={this.state.users} /> needs to be <UserList users={this.props.users} /> As already mentioned by #Nsevens
You are mapping redux state into your component's props.
So you should load the users from the component's props and not it's state:
render(){
return (
<UserList users={this.props.users} />
)
}
I am getting the response from backed which looks something like this.
But when i try to log the data like this.
render() {
const { reviews } = this.props;
console.log('rev', reviews.reviewList.data._embedded);
It gives me error saying this.
TypeError: reviews.reviewList.data is undefined
reviewDataReducer.jsx
const initialstate = {
isFetching: false,
reviewList: [],
page: null,
fetched: false,
error: null
};
export default (state = initialstate, action) => {
switch (action.type) {
case actionTypes.GET_PRODUCT_REVIEWS_LOAD:
return {
...state,
isFetching: true
};
case actionTypes.GET_PRODUCT_REVIEWS_SUCCESS:
return {
...state,
fetched: true,
reviewList: action.payload
};
case actionTypes.GET_PRODUCT_REVIEWS_ERROR:
return {
...state,
fetched: false,
isFetching: false,
error: action.error
};
default:
return state;
}
};
reviewActions.jsx
export const getProductReviews = pid => dispatch => {
console.log('rev pid',pid)
dispatch({
type: types.GET_PRODUCT_REVIEWS_LOAD
});
new _rest()
.get(`/buyer/product/${pid}/review`)
.then(res => {
console.log("Review Action Response", res);
dispatch({
type: types.GET_PRODUCT_REVIEWS_SUCCESS,
payload: res
});
})
.catch(error => {
dispatch({
type: types.GET_PRODUCT_REVIEWS_ERROR,
error: error
});
});
};
connect
const mapStateToprops = state => ({
reviews: state.reviews.data
});
const mapStateToDispatch = {
getProductReviews
};
export default connect(
mapStateToprops,
mapStateToDispatch
)(ReviewContainer);
Your information is limited but I will try to be best.
1] Error is because you'r traversing object wrong not because data in not there in this case.
2] render() {
const { reviews } = this.props;
Here I feel you mapping redux state to prop using (mapStateToProps) if so reducer is responsible how you set data in redux state.
The issue is there inside the mapStateToprops connection. Try to debug it there.
const mapStateToprops = state => {
debugger;
return ({
reviews: state.reviews.data
});
};
Open your browser console and check the value of state;
I create app with react and redux and I need to fetch data. Is there any way to reuse function getData() and reducer. My actions looks like this
importing constants
const getDataRequested = () => {
return {
type: GET_DATA_REQUESTED
};
}
const getDataDone = data => {
return {
type: GET_DATA_DONE,
payload: data
};
}
const getDataFailed = () => {
return {
type: GET_DATA_FAILED
};
}
export const getData = () => dispatch => {
dispatch(getDataRequested());
fetch('url')
.then(response => response.json())
.then(data => {
dispatch(getDataDone(data));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
and reducer
importing constants
const initialState = {
isLoading: false,
isError: false,
data: [],
}
export default (state=initialState, action) => {
switch (action.type) {
case GET_DATA_REQUESTED:
return { ...state, isLoading: true };
case GET_DATA_DONE:
return { ...state, isLoading: false, data: action.payload };
case GET_DATA_FAILED:
return { ...state, isLoading: false, isError: true}
default:
return state;
}
};
Every time I fetch something with different url I create new action and new reducer. Is it ok or there is some way to reuse it?
You can pass a url parameter to your thunk. So, you could have something like this:
export const getData = (url) => dispatch => {
dispatch(getDataRequested());
fetch(url)
.then(response => response.json())
.then(data => {
dispatch(getDataDone(data));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
This way you can dispatch as many actions as you want changing only the url parameter, like this: getData('/user'), getData('/products').
You can also customize the way you store the state into redux by passing more parameters to the thunk. So it could be something like this:
const getDataDone = data => {
return {
type: GET_DATA_DONE,
payload: data
};
}
export const getData = (url, stateName) => dispatch => {
dispatch(getDataRequested());
fetch(url)
.then(response => response.json())
.then(data => {
dispatch(getDataDone({ stateName: data }));
})
.catch(error => {
dispatch(getDataFailed(error));
})
}
And the reducer could be something like this:
const initialState = {
isLoading: false,
isError: false,
data: {},
}
export default (state=initialState, action) => {
switch (action.type) {
case GET_DATA_REQUESTED:
return { ...state, isLoading: true };
case GET_DATA_DONE:
return { ...state, isLoading: false, [action.payload.stateName]: action.payload.data };
case GET_DATA_FAILED:
return { ...state, isLoading: false, isError: true}
default:
return state;
}
};
That way you can dispatch actions like getData('/user', 'user') or getData('/products', 'products') and have a state like this:
{
user: {
// your users data
},
products: {
// your products data
}
}
You can combine all actions in one function like
function getData() {
return {
types: [GET_DATA_REQUESTED, GET_DATA_DONE, GET_DATA_FAILED],
callApi: fetch('/')
}
}
but you need to connect your component and pass the function as props
function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(getData())
};
}
connect(null, mapDispatchToProps)(YourComponent)
now you can use the function in your component and it will return a promise.
check out the Docs for redux: https://github.com/reactjs/react-redux