I'm trying to implement, let's say Twitter. I'm doing something like
initialState = {
tweets: {id => tweet}
}
then, a user goes to his timeline, now the action fetchTweets fetches all his tweets. However, then he can post a tweet, tweet T. But if I don't manually insert the posted T into state.tweets, he will not see this tweet in his timeline.
So here comes the question, when a user did some actions on his page, is that a good point to refresh the data? How does redux avoid data stale in this kind of case?
Thanks!
It's a bit of a paradigm shift when using Redux, but you don't want any functions to be your state. So, that initial state should actually just be either null or an empty object (depending on the design of the components that receive those props). But to answer your question a bit more directly, if you want to make sure that data stays "fresh" you need to make it happen. There is no magic in Redux, which is a GOOD THING.
However, if you design your code properly, the user shouldn't experience something resembling a full page refresh. At a high level, here is how I might design what you are describing.
Write actions for requesting and receiving tweets, i.e.:
export function requestTweets() => {
return {
type: REQUEST_TWEETS
}
}
export function receiveTweets(tweets) => {
return {
type: RECEIVE_TWEETS,
payload: tweets
}
}
Then wrap that in a "public" function that can be reused wherever:
export const fetchTweets = () => {
return (dispatch, getState) => {
dispatch(requestTweets())
//network code here is pseudocode
fetch('https://tweeter/api/v1/tweets')
.then(data => dispatch(receiveTweets(data))
.catch(err => displayErrorMsg(err))
}
}
Then in your action handlers just reduce the tweets into the next state.
Now with fetchTweets, you can call that on the first load, after posting or on an interval. The nice thing is React will handle the diffing well for you and not re-render the entire page. It's just up to you to design it well so the user notices when and where new tweets come in.
Hope that helps!
Redux manages your state for you. It tells anyone who wants to hear about any changes in the state, such as React-Redux.
It does not do anything to help you with getting data, such as tweets. Nor does it help you decide when to get this data.
You'll have to decide for yourself when to do this and you can use setTimeout or anything else you feel like.
It's probably best if you manually insert the tweet into the state rather than refetch when you post. It's so much more responsive.
Related
I have read multiple articles on the need to use Redux and have built two fully-functioning React+Redux applications. I have even posted the question on Quora I still cannot have a final answer to my question:
Do I have to save every component state property to the Redux store?
The first project, I have built by following a tutorial where he basically saves everything to the store.
Here's the Github link.
Since I was learning React and Redux, I did not question this approach and went on with it. But, it does seem somewhat unnecessary to save everything to the store
For example, there's an action that saves the comment data to the store:
postActions.js
// Add Comment
export const addComment = (postId, commentData) => dispatch => {
dispatch(clearErrors());
axios
.post(`/api/posts/comment/${postId}`, commentData)
.then(res =>
dispatch({
type: GET_POST,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
And it is called like this:
CommentForm.js
onSubmit(e) {
e.preventDefault();
const { user } = this.props.auth;
const { postId } = this.props;
const newComment = {
text: this.state.text,
name: user.name,
avatar: user.avatar
};
this.props.addComment(postId, newComment);
this.setState({ text: '' });
}
If I were working on my own project, I would've kept the message data stored locally at the component level:
The second project was a personal project, where the only data I saved in the store, is the user account information because I would need it in different components throughout the app to send it in some backend API requests.
All the other components are basically independent or the flow between them does not go beyond two or three components. So I really could not see why I would make myself code all the actions, reducers...etc for all of the components. So I simply pass the props and functions between components in the plain old react way of doing things.
Most of the answers that I found do not go into this specific detail mentioned in my question. All of them talk from a high-level perspective.
Before going ahead and working on other projects, I would like to :
A clear answer to my question
Whether the approach I used for my personal project is okay. In other words, can I use Redux simply for the user account information and for the rest of the components not use it?
I just want to clear this confusion so that when I am using Redux, I am 100% sure, I am using it because I actually need it.
Do I have to save every component state property to the Redux store?
Short answer: No you don't.
Longer answer: To quote Dan Abramov on a similar question:
Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state.
Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.
There is nothing wrong with the approach taken in your personal project. Redux is great for storing/sharing global application state, such as the user info you describe.
Before putting state into Redux I'd ask:
Will this state be consumed by other components independent to this one?
If the answer to #1 is yes: then ask how often?
If the answer to #2 is frequently: then ask is a single source of truth (the Redux store) the best way to share this particular piece of state? Would
other techniques (hooks / render props / higher order components) be more appropriate?
Another quote from Dan in the same linked thread is:
If it gets tedious and frustrating don’t be afraid to put state into the components. My point is that use single state tree unless it is awkward, and only do this when it simplifies things for you rather than complicates them. That’s the only guideline.
The mantra Yagni (You Aren't Gonna Need It) springs to mind.
If you're unsure whether state should be abstracted from a component into Redux, then the chances are it's too early todo so. This helps avoid making design decisions too early, whilst keeping your Redux state lean and intentional (i.e: not convoluted with unnecessary single use concerns).
Ultimately the cost of putting state into Redux needs to pay off.
New to react/redux so bear with me :).
When selecting a regionlevel from a dropdown, I want to store the selected RegionLevelId, and make an API call to fetch the regions that belong to this RegionLevel.
At first I was storing this regionLevelId in the regionLevelReducer, which made it so that I only triggered one state change.
However, I'm saving more selected options in my state so I figured it might be cleaner to make a "selectOption" reducer that stores the selectedIds.
The point is now however, that when I select a regionLevel from the dropdown, I have to make two action calls. One to store the regionLevelId, and one to fetch data from the API. This renders the page twice, which is unnecessary.
I was wondering if it's possible to call two actions, while rendering AFTER these two actions have been completed. Now it renders in between these two actions.
I'm not sure if this is defying of how redux should be used. But I'm interested in how people would solve this problem.
The action I'm using atm:
export function selectRegionLevel(regionLevelId) {
return function(dispatch) {
dispatch({
type: "SELECT_REGION_LEVEL",
payload: regionLevelId
});
dispatch({
type: "FETCH_REGIONS",
payload: {
request: {
url: `regionLevels/${regionLevelId}/regions`
}
}
});
};
}
Thanks.
From your requirement, i think what you are looking for is Batched Action. Take a look at react-batched-actions which aims to solve this problem and is recommended by redux team in such scenerios.
I know Redux solves this but I came up with an idea.
Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.
Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.
Why can't I do something like this:
fetch(listItems)
.then(response => response.json())
.then(json => {
window.consts = json.list;
This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.
What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:
// get-timezones.js
import { get } from '../services/request'
let fetching = false
let timez = null
export default () => {
// if we already got timezones, return it
if (timez) {
return new Promise((resolve) => resolve(timez))
}
// if we already fired a request, return its promise
if (fetching) {
return fetching
}
// first run, return request promise
// and populate timezones for caching
fetching = get('timezones').then((data) => {
timez = data
return timez
})
return fetching
}
And then in the view react component:
// some-view.js
getTimezones().then((timezones) => {
this.setState({ timezones })
})
This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).
Your approach may have a few issues:
If react renders before this window.consts is populated you won't
be able to access it, react won't know it should re-render.
You seem to be doing this request even when the data won't be used.
The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.
From the React point of view:
You can pass the list from top level via Context and you can see docs here.
Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.
From engineering concept of everything is a trade of:
If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)
If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..
Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference
I'm learning to use React + Redux + react-router-redux. The project is a toy project, and there are only two routes:
<Route path="/" component={ItemList} />
<Route path="/:id/" component={ItemView} />
First, I have implemented basic operation: when user clicks on ItemList, I call dispatch(push(/${id}/)). Then, using <Router onChange={...}> I detect the navigation and dispatch an action to fetch item's data from server. When the data arrives I can finally put it in my state and ItemView updates from just saying "Loading..." to a proper item display.
However, I want things shiny and fancy. On click, I want to start fetching data but keep old UI (the item list) until the data arrives, and only then - having the data - quickly switch the route. Just like GitHub or GitLab do. So I had researched a bit, threw in redux-thunk, redux-promise-middleware and react-redux-loading-bar and wrote some code.
In the list component, I have what's essentially onClick={dispatch(getItem(id))} where getItem is:
export function getItem(id) {
return function (dispatch) {
return dispatch({
type: GET_ITEM,
payload: new Promise(resolve => {
fetch(`/${id}/?json`)
.then(response => resolve(response.json()))
})
}).then(() => {
dispatch(push(`/${id}/`));
})
}
}
When the promise is resolved, redux-promise-middleware
generates a 'GET_ITEM_FINISHED' action for me. So, in my reducer,
I just put the data I got from server into state:
...
case 'GET_ITEM_FINISHED':
return Object.assign({}, state, {
item: action.payload.item
});
...
(The code above had evolved a few dozen of trial-and-error iterations, so it's not clean. I know, it could be prettier, sorry about this.)
It seem to work at first glance, but this way fetching data is completely detached from navigation events - and that's bad. Whenever there's a navigation event that happens from any cause other than getItem dispatch (e.g. back/forward buttons), there won't be any fetches and necessary state transitions. This means, the approach I've taken is wrong, on the concept level.
I'm sort of stuck thinking of how to do it. My only thought is that the state should be like a cache that can "hit" (have the item data at navigation event's time) or "miss" (don't have it). When I have the data - like in my code above - I can readily display it. When there's a miss, things get hairy - either a reducer or a component would have to dispatch a "GET_ITEM" action, but while this would probably work, it somehow feels like a bad idea.
Basically, all I want is that red thin "loading" line when navigation's within my UI, but also proper handling of back/forward buttons (or whatever may change location). When user navigates directly to the page (full page load), the backend will make data available in HTML it serves, as store's preloadedState.
What I need is a high-level idea/overview of how such things are generally done properly. Or a pointer to any library that implements such idea, if there is anything readily available.
I'm have trouble figuring out how to structure my code with redux. Here's the high level flow that I'm looking at:
An action execute successfully, and triggers history.push('/something')
That leads a new component Something to be loaded
In Something.componentWillMount(), I want to fetch some data for the component, so I call this.props.loadSomething(), which is another action
Step (3) is the problem. Since the history.push() call is in an action, redux doesn't let me call this.props.loadSomething(), since that would be calling an action within an action.
What is the proper way to handle this? I feel like this must be a very common problem, so there should be a standard approach for it.
What you're looking at is indeed a common scenario. The easy way around the problem is the use of a thunk.
It is basically built so that you can dispatch multiple actions simultaneously and they can even be asynchronous.
You can find relevant documentation here.
Sample usage in an app, an action creator:
export function doSomething(someParam, someOtherParam) {
return dispatch => {
dispatch(someOtherActionCreator(someParam));
dispatch(loadMyDataActionCreator(someParam, someOtherParam));
};
}
You could possibly only redirect once you've loaded the data. You can also do that with thunks.
export function fetchMeStuffNRedirect(params) {
return dispatch => {
return dispatch(loadSomeStuffThatReturnsPromise())
.then(result => dispatch(doSomethingWithResult(result))
.catch(error => dispatch(awSnapActionCreator(error));
};
}