I'm new to Redux. And I'm trying to create a simple FETCH_ALL_POSTS.
actions
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
console.log(response.data)
dispatch({
type: FETCH_ALL_POSTS,
payload: response.data
})
}
posts reducer
export default (state = {}, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return {
...state, payload
}
default:
return state
}
}
post list component
const mapStateToProps = state => {
console.log(Object.values(state.posts))
return {
posts: state.posts
}
}
This is working but the data that I'm getting from mapStateToProps is not what I'm expecting.
Result : "array: [ 0:[{},{},{}] ]"
My expected result: "array:[{},{},{}]"
Try this,
const initialState = {
posts: '',
}
export default (state=initialState, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return{
posts:state.posts=action.payload.posts
}
default:
return state
}
}
Related
I am using Redux for state management, I have faced an issue in reducer function
here is the image of my console, You can see the Product Action is providing my data but the reducer is not passing on my function
here is my code of ProductAction:
export const getProductsbyFind = (myvariable) =>async (dispatch)=>{
try {
console.log(myvariable)
dispatch({type: ALL_PRODUCTS_REQUEST_BY_ID})
const{ data } = await axios.get(`/api/v1/product/${myvariable}`)
console.log(data)
dispatch({
type: ALL_PRODUCTS_SUCCESS_BY_ID,
payload: data
})
} catch (error) {
dispatch({
type:ALL_PRODUCTS_FAIL,
payload: error.response.data.message
})
}
}
here is the code of Reducer:
export const productReducersById = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCTS_REQUEST_BY_ID:
return {
loading: true,
products: []
}
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount
}
case UPDATE_QUANTITY_BY_ID:
const { index, quantity } = action.payload;
const prods = state.products.map((p, i) => {
if (i !== index)
return p;
return {
...p,
quantity
}
});
return {
loading: true,
products: prods
}
case ALL_PRODUCTS_FAIL_BY_ID:
return {
loading: false,
error: action.payload
}
case CLEAR_ERRORS_BY_ID:
return {
...state,
error: null
}
default:
return state
}
}
here is the code of my page where I want to get my data:
const { loading, products, error, productCount } = useSelector(state => state.products);
console.log(products)
useEffect(() => {
dispatch(getProductsbyFind(myvariable));
}, [dispatch])
You have a typo in your reducer:
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
- products: action.payload.products,
+ products: action.payload.product,
productsCount: action.payload.productsCount
}
(Also, productsCount does not exist in your payload, so that will become undefined.)
i've been having troble getting my nextjs app to work with getServerSideProps() for server-side-rednering. i tried implemening next-redux-wrapper but the state is empty.
*note: redux works fine while it was running on client side, but now im trying to get the state in getServerSideProps() and pass it into the component, so it renders on the server.
store.js:
const reducer = combineReducers({
productList: productListReducer,
categoryList: categoryListReducer,
})
const middleware = [thunk]
const makeStore = context => createStore(reducer, composeWithDevTools(applyMiddleware(...middleware)))
const wrapper = createWrapper(makeStore, {debug: true})
export default wrapper
reducer.js:
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case HYDRATE:
return {...state, ...action.payload}
case 'PRODUCT_LIST_REQUEST':
return { loading: true, products: [] }
case 'PRODUCT_LIST_SUCCESS':
return { loading: false, products: action.payload }
case 'PRODUCT_LIST_FAIL':
return { loading: false, error: action.payload }
default:
return state
}
}
_app.js:
import wrapper from '../redux/store'
function MyApp({ Component, pageProps }) {
return (
<Component {...pageProps} />
)
}
export default wrapper.withRedux(MyApp)
index.js:
import wrapper from '../redux/store'
export const getServerSideProps = wrapper.getServerSideProps(store => ({req, res}) => {
const state = store.getState()
const { products } = state.productList
return {props: {products: products}}
})
export default function Home({products}) {
return (
<>
<div>{products}</div>
</>
)
}
page.js:
const mapDispatchToProps = (dispatch) => {
return {
listProducts: bindActionCreators(listProducts, dispatch),
listCategories: bindActionCreators(listCategories, dispatch)
}
}
function mapStateToProps(state) {
return {
products: state.productList.products,
loading: state.productList.loading,
categories: state.categoryList.categories,
loadingCategory: state.categoryList.loading
}
}
CategoryScreen.getInitialProps = wrapper.getInitialPageProps((store) => async () => {
await store.dispatch(listProducts())
await store.dispatch(listCategories())
})
export default connect(mapStateToProps, mapDispatchToProps)(CategoryScreen)
reducer.js:
import { HYDRATE } from "next-redux-wrapper"
export const categoryListReducer = (state = { categories: [] }, action) => {
switch (action.type) {
case HYDRATE:
return {...state, ...action.payload}
case 'CATEGORY_LIST_REQUEST':
return { loading: true, categories: [] }
case 'CATEGORY_LIST_SUCCESS':
return { loading: false, categories: action.payload }
case 'CATEGORY_LIST_FAIL':
return { loading: false, error: action.payload }
default:
return state
}
}
store.js:
const combinedReducer = combineReducers({
productList: productListReducer,
categoryList: categoryListReducer
})
const reducers = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer(state, action)
}
}
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
export const makeStore = (context) => {
const store = createStore(reducers, bindMiddleware([thunk]))
return store
}
export const wrapper = createWrapper(makeStore, { debug: true })
I have a problem with Redux doesn't update the state. Component gets right initial state. Action is dispatched right, data is fetched right and is accesible in action payload inside reducer. Reducer is executing, right case in switch is picked. Just new state doesn't appear in component. I have three others components where it works just fine, only this one cracks.
component
import fetchLinksPage from '../state/links/actions'
...
let Links = ({linksPageLoaded, linksPage, fetchLinksPage}) => {
useEffect( () => {
if(!linksPageLoaded) {
fetchLinksPage()
console.log(linksPage)
}
},[])
return ( ... )
}
const mapStateToProps = ({linksPageReducer}) => {
return linksPageReducer
}
const mapDispatchToProps = dispatch => {
return {
fetchLinksPage: () => dispatch(fetchLinksPage())
}
}
Links = connect(mapStateToProps, mapDispatchToProps)(Links)
actions
// action types
export const GET_LINKS_PAGE = 'GETLINKSPAGE'
export const LINKS_PAGE_LOADED = 'LINKSPAGELOADED'
export const LINKS_PAGE_ERROR = 'LINKSPAGEERROR'
// action creators
export const getLinksPage = () => {
return {
type: GET_LINKS_PAGE
}
}
export const linksPageLoaded = (data) => {
return {
type: LINKS_PAGE_LOADED,
payload: data
}
}
export const linksPageError = (error) => {
return {
type: LINKS_PAGE_ERROR,
payload: error
}
}
const fetchLinksPage = () => {
return dispatch => {
dispatch(getLinksPage())
fetch('http://portfolio.adamzajac.info/_/items/links?fields=*,logo.*.*')
.then(response => response.json())
.then(data => {
dispatch(linksPageLoaded(data.data))
})
.catch( error => {
dispatch(linksPageError(error))
})
}
}
export default fetchLinksPage
reducer
import * as actions from './actions.js'
const linksPageReducer = (state={}, action) => {
switch (action.type) {
case actions.GET_LINKS_PAGE:
return { ...state, linksPageLoading: true }
case actions.LINKS_PAGE_LOADED:
//console.log('update state')
return { ...state, linksPage: action.payload, linksPageLoading: false, linksPageLoaded: true }
case actions.LINKS_PAGE_ERROR:
return { ...state, linksPageError: action.payload, linksPageLoading: false}
default:
return { ...state, linksPageLoading: false, linksPageLoaded: false, linksPage:[], linksPageError:''}
}
}
export default linksPageReducer
store
import aboutPageReducer from './state/about/reducer'
import projectsPageReducer from './state/projects/reducer'
import skillsPageReducer from './state/skills/reducer'
import linksPageReducer from './state/links/reducer'
const rootReducer = combineReducers({
aboutPageReducer,
projectsPageReducer,
skillsPageReducer,
linksPageReducer
})
const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
How can I fetch only one data and write it to Header ?
I am using firebase and react-redux.
firebase structure i try to write "organization": inovanka:
Action File Codes:
import firebase from 'firebase';
import { Actions } from 'react-native-router-flux';
import { ORGANIZATION_NAME_DATA_SUCCESS } from './types';
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: snapshot.val() });
});
};
}
Reducer File :
import { ORGANIZATION_NAME_DATA_SUCCESS } from '../actions/types';
const INITIAL_STATE = {
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action); // data retrieved as array
return action.payload
default:
return state;
}
};
Component: (I would like to write it to this)
class HomePage extends Component {
componentWillMount() {
}
render() {
return (
<Container>
<Header>
<Text> i would like to write it here </Text>
</Header>
<Content>
</Content>
</Container>
);
}
}
const mapStateToProps = ({ homepageResponse }) => {
const organizationArray = _.map(homepageResponse, (val, uid) => {
return { ...val, uid }; //
});
return { organizationArray };
};
export default connect(mapStateToProps, { organizationName })(HomePage);
Change this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
to this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
using once() will read data only one time, thus fetching only one data
Solution is Here !
Action File:
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
_.mapValues(snapshot.val(), o => {
console.log(o);
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: {organization: o.organization, fullname: o.fullname }});
});
});
};
}
Reducer File
const INITIAL_STATE = {
organization: '',
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action);
return {...state, organization:action.payload.organization };
default:
return state;
}
};
Component File MapToStateProps and componentWillMount
const mapStateToProps = state => {
const { organization, fullname } = state.homepageResponse;
console.log("burada" + organization);
return { organization, fullname };
};
componentWillMount(){
this.props.organizationName();
}
*Last Step Header *
render() {
return (
<Container>
<Header>
<Text> { this.props.organization } </Text>
</Header>
</Container>
}
Thank You Everyone
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