I have a react application and I try to implement redux within this application.
So my files look like this:
reducer.js
import {
CHANGE_SEARCH_FIELD,
REQUEST_ROBOTS_FAILED,
REQUEST_ROBOTS_PENDING,
REQUEST_ROBOTS_SUCCESS
} from './constants';
const initialState = {
searchField: ''
}
export const searchRobots = (state = initialState, action = {}) => {
console.log(action.type);
switch (action.type) {
case CHANGE_SEARCH_FIELD:
return Object.assign({}, state, { searchField: action.payload });
default:
return state;
}
}
const initialStateRobots = {
robots: [],
isPending: true
}
export const requestRobots = (state = initialStateRobots, action = {}) => {
switch (action.type) {
case REQUEST_ROBOTS_PENDING:
return Object.assign({}, state, { isPending: true })
case REQUEST_ROBOTS_SUCCESS:
return Object.assign({}, state, { robots: action.payload, isPending: false })
case REQUEST_ROBOTS_FAILED:
return Object.assign({}, state, { error: action.payload })
default:
return state
}
}
actions.js
import {
CHANGE_SEARCH_FIELD,
REQUEST_ROBOTS_FAILED,
REQUEST_ROBOTS_PENDING,
REQUEST_ROBOTS_SUCCESS
} from './constants'
export const setSearchField = (text) => ({
type: CHANGE_SEARCH_FIELD,
payload: text
});
export const requestRobots = () => (dispatch) => {
dispatch({ type: REQUEST_ROBOTS_PENDING })
fetch('https://jsonplaceholder.typicode.com/users')
.then(data => dispatch({ type: REQUEST_ROBOTS_SUCCESS, payload: data }))
.catch(error => dispatch({ type: REQUEST_ROBOTS_FAILED, payload: error }))
}
index.js:
const logger = createLogger();
const rootReducer = combineReducers({searchRobots, requestRobots});
const store = createStore(rootReducer, applyMiddleware( thunkmiddleware,logger));
ReactDOM.render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
and app.js:
const mapStateToProps = state => {
return {
searchField: state.searchField,
robots: state.requestRobots.robots,
isPending: state.requestRobots.isPending
}
};
const mapDispatchToProps = (dispatch) => {
return {
onSearchChange: (event) => dispatch(setSearchField(event.target.value)),
onRequestRobots: () => dispatch(requestRobots())
}
}
function App(props) {
useEffect(() => {
props.onRequestRobots();
console.log(props);
}, []);
const filteredRobots = robots.filter(robot => {
return robot.name.toLowerCase().includes(searchField.toLowerCase());
});
return !robots.length ?
<h1>Loading</h1> :
(
<div className='tc'>
<h1 className='f1'>RoboFriends</h1>
<SearchBox searchChange={props.onSearchChange} />
<Scroll>
<CardList robots={filteredRobots} />
</Scroll>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
But I get this errror:
ReferenceError: robots is not defined
App
E:/Mijn Documents/UDEMY/REACT/robofriends-master/src/containers/App.js:40
37 |
38 |
39 | //const { robots, searchField, onSearchChange, isPending } = props;
> 40 | const filteredRobots = robots.filter(robot => {
| ^ 41 | return robot.name.toLowerCase().includes(searchField.toLowerCase());
42 | });
43 | return !robots.length ?
my question: what I have to change?
Thank you
You should get robots from props like this:
const filteredRobots = props.robots.filter(robot => {
return robot.name.toLowerCase().includes(searchField.toLowerCase());
});
Related
I have a react component that fetches from API with createAsyncThunk and cannot understand a behaviour that happens on Mount in this part:
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
if(feedResponse) return(
<div className={styles.feed}>
{feedResponse.map(({id}) => {
return(
<Link to={`/thread=${id}`} key={id}>
<Thread key={id} id={id}/>
</Link>
)
})}
</div>
)
If I remove if(feedResponse) next to the return(...) the component will crash because it will try to render before feedResponse status has data. Why isn't that covered by the first two IFs?
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
It is my understanding that there is no scenario where we have an inLoading = false & feedResponse = null
Here is the complete code if needed:
Feed.js
export const Feed = () => {
const dispatch = useDispatch()
const feedResponse = useSelector(selectFeedResponse)
const isLoading = useSelector(isLoadingFeed)
const failedToLoad = useSelector(failedToLoadFeed)
const location = useLocation()
useUpdateEffect(() => {
dispatch(SearchThunk(searchTerm))
}, searchTerm)
useUpdateEffect(() => {
dispatch(homeThunk(location.pathname+'.json'))
}, location)
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
if(feedResponse) return(
<div className={styles.feed}>
{feedResponse.map(({id}) => {
return(
<Link to={`/thread=${id}`} key={id}>
<Thread key={id} id={id}/>
</Link>
)
})}
</div>
)
}
feedSlice.js
export const homeThunk = createAsyncThunk(
'feed/homeThunk',
async (homePath) => {
const response = await fetch(`https://www.reddit.com${homePath}`)
const json = await response.json()
const threads = json.data.children.map(thread => {
return {
id: thread.data.id,
subreddit: thread.data.subreddit,
title: thread.data.title,
author: thread.data.author,
thumbnail: thread.data.thumbnail,
created: thread.data.created,
score: thread.data.score,
num_comments: thread.data.num_comments
}
})
return threads
}
)
export const feedSlice = createSlice({
name: 'feed',
initialState: {
feedResponse: '',
isLoadingFeed: false,
failedToLoadFeed: false
},
extraReducers: (builder) => {
builder
.addCase(homeThunk.pending, (state) => {
state.isLoadingFeed = true
state.failedToLoadFeed = false
})
.addCase(homeThunk.fulfilled, (state, action) => {
state.isLoadingFeed = false
state.failedToLoadFeed = false
state.feedResponse = action.payload
})
.addCase(homeThunk.rejected, (state) => {
state.isLoadingFeed = false
state.failedToLoadFeed = true
})
}
})
export const selectFeedResponse = state => state.feed.feedResponse
export const isLoadingFeed = state => state.feed.isLoadingFeed
export const failedToLoadFeed = state => state.feed.failedToLoadFeed
export default feedSlice.reducer
feedUtilities.js
import { useEffect, useRef } from "react";
export const useUpdateEffect = (effect, deps = []) => {
const isFirstMount = useRef(true);
useEffect(() => {
if(!isFirstMount.current) effect()
else isFirstMount.current = false
}, [deps]);
}
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 })
When I console on line 21 of Bugs.js, its sowing ,me the result of action that i dispathed, but when I am printing the data on in li tag..it's blank. Also the store is not chnaged in redux-dev-tools
That's my App.js code
function App() {
const store = configureStore()
return (
<Provider store={store}>
<Bugs />
</Provider>
);
}
export default App;
That's Bugs.js
const Bugs = (props) => {
useEffect(props.addBug, [])
return (
<ul>
{
props.bugs.map(bug => {
<li>
{bug.description}
</li>
})
}
</ul>
)
}
const mapStateToProps = state => {
console.log('Aftab', state.bugs);
return (
{
bugs: state.bugs
})
}
const mapDispatchToProps = dispatch => {
return (
{
addBug: () => dispatch(addBug({ description: 'Bug 1' }))
}
)
}
export default connect(mapStateToProps, mapDispatchToProps)(Bugs)
And thats reducer
let lastId = 0
const slice = createSlice({
name: 'bugs',
initialState: [],
reducers: {
addBug: (bugs, action) => {
bugs.push({
id: lastId++,
description: action.payload.description,
resolved: false
})
},
resolveBug: (bugs, action) => {
const index = bugs.findIndex(b => b.id === action.payload.id)
bugs[index].resolved = true
},
assignBug: (bugs, action) => {
const index = bugs.findIndex(b => b.id === action.payload.bugId)
bugs[index].userId = action.payload.userId
}
}
})
export const { addBug, resolveBug, assignBug } = slice.actions
export default slice.reducer
export const getBugList = userId => createSelector(
state => state.entities.bugs,
bugs => bugs.filter(b => b.userId === userId)
)
You will need to invoke the function for dispatch to work
In your Bugs.js change
useEffect(props.addBug, [])
To:
useEffect(props.addBug(), [])
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