How do I maintain a central/global object for some data with react-router?
Let's say my React site has 20 different routes. 15 out of those 20 routes need one JSON file from the server that almost never changes. I hate having to GET "/api/mydata" for every new URL/route.
I guess I can save the data in React's context, but that seems hacky and may not even work. Is there a more elegant solution for sharing data between react-router components?
Edit: I partially solved this by creating a Data.js file:
export var myData = function(cb) {
ajax('GET', '/api/mydata', resp => {
myData = function() {
return cb(resp);
};
cb(resp);
});
};
and use it like this:
myData(data => data.map(/* ...use the data */));
That way the data is guaranteed to only be fetched once per lifecycle.
Arrr... if you don't want to use redux or flux, you can create a singleton like this in a new js file
let appState = {};
export default appState;
First do ajax to fill the data into this singleton. Then import it to everywhere you want the data.
Related
In React server components official GitHub example repo at exactly in this line here they are using response.readRoot().
I want to create a similar app for testing something with RSC's and it seems like the response does not contain the .readRoot() function any more (because they have updated that API in the react package on npm and I cannot find anything about it!). but it returns the tree in value property like below:
This means that whatever I render in my root server component, will not appear in the browser if I render that variable (JSON.parse(value) || not parsed) inside of my app context provider.
How can I render this?
Basically, if you get some response on the client side (in react server components) you have to render that response in the browser which has the new state from server but since I don't have access to readRoot() any more from response, what would be the alternative for it to use?
I used a trick o solve this issue, but one thing to keep in mind is that they are still unstable APIs that react uses and it's still recommended not to use React server component in the production level, uses it for learning and test it and get yourself familiar with it, so back to solution:
My experience was I had a lot of problems with caching layer they are using in their depo app. I just removed it. My suggestion is to not use it for now until those functions and APIs become stable. So I Removed it in my useServerResponse(...) function, which in here I renamed it to getServerResponse(...) because of the hook I created later in order to convert the promise into actual renderable response, so what I did was:
export async function getServerResponse(location) {
const key = JSON.stringify(location);
// const cache = unstable_getCacheForType(createResponseCache);
// let response = cache.get(key);
// if (response) return response;
let response = await createFromFetch(
fetch("/react?location=" + encodeURIComponent(key))
);
// cache.set(key, response);
return response;
}
and then creating a hook that would get the promise from the above function, and return an actual renderable result for me:
export function _useServerResponse(appState) {
const [tree, setTree] = useState(null);
useEffect(() => {
getServerResponse(appState).then((res) => {
setTree(res);
});
}, [appState]);
return { tree };
}
and finally in my AppContextProvider, I used that hook to get the react server component tree and use that rendered tree as child of my global context provider in client-side like below:
import { _useServerResponse } from ".../location/of/your/hook";
export default function AppContextProvider() {
const [appState, setAppState] = useState({
...someAppStateHere
});
const { tree } = _useServerResponse(appState);
return (
<AppContext.Provider value={{ appState, setAppState }}>
{tree}
</AppContext.Provider>
);
}
I know that this is like a workaround hacky solution, but it worked fine in my case, and seems like until we get stable APIs with proper official documentation about RSCs, it's a working solution for me at least!
I am building an application with storefront, which uses nextJS. I am able to use getServerSide props while loading a new page.
The page contains many components, each needing their own data. At the moment, I am getting all of these into a results array and then returning from getServerSideProps, as shown below.
export async function getServerSideProps({query}) {
let sid = query.shop
let promises = []
promises.push(getShopCategories(sid))
promises.push(getShopInfo(sid))
promises.push(offersFromShop(sid))
try {
let allPromises = Promise.all(promises)
let results = await allPromises;
//console.log("Shop Info:", results[1])
return {props: {
id: sid,
shopCategories: results[0],
shopInfo: results[1],
offers4u: results[2].products
}}
} catch(e) {
console.error("Failure:", e)
return { props: {}}
}
}
But in this way, I have to get the data needed for all components in one shot. I was thinking, how to let each sub component in the page to have its own 'getServerSideProps'.
I can implement this in each component, but I am unsure about passing the parameters needed (such as shopId, productId etc., which I would have fetched in the main page).
One way is to use cookies, so that the server side can pick up these values. Is there a better solution?
getServerSideProps is only available on direct component under page folder
If you want to fetch more data on sub-component in the page, consider using the useEffect hook or componentDidMount for that work.
I'm following this tutorial for Apollo here: https://github.com/howtographql/react-apollo/blob/master/src/components/Header.js.
In the Header.js file they have:
const userId = localStorage.getItem(GC_USER_ID)
which seems to me a bit ugly: everytime Header render() is called you read from localStorage?
With react-redux I used to go with something like this:
render() {
const { authentication } = this.props;
...
}
const mapStateToProps = state => ({
authentication: state.authentication
});
export default connect(mapStateToProps, { logoutAction })(Navbar);
And also if I read from localStorage on every render() and let's say I wanna store in localStorage all my user's data (eg. {username: "john", age: "15"}) when I pass this const to one of my children components everytime it re-render because the JSON.parse makes it a new object! Everytime!
How to have something like a Redux Store with Apollo?
Apollo's solution for managing state is through apollo-link-state. Here is some general info and the docs. After setting it up, you can then query state within a GraphQL query.
It has not reached 1.0 release yet, but it's usable and fairly easy to setup. Their examples aren't great yet. However, if you have some familiarity with GraphQL resolvers, it's fairly simple to setup. I'm using it here with success.
I'm trying to implement an external API library in a redux application.
I'm fresh new in redux so I don't know exactly how it works.
In my javascript using the API library, I wan't to access info from a container (the user firstanme if he's logged).
After reading some doc, I tried to import the store in my js file, to get the state of the user, but I can't reach the info I need.
Here's the code I tried :
import configureStore from '../store/configureStore';
const store = configureStore();
const state = store.getState();
I get many info in state, but not the one I need. Any help ?
First of all it looks like configureStore creates new store every time you call it. But you need the very that store that your components will use and populate. So you need to somehow access the store you are passing your Provider.
Then since store state is "changing" you can't simply read it once. So your user data might be initially empty but available some time later.
In this case you could make it a Promise
const once = selector => available => new Promise(resolve => {
store.subscribe(() => {
const value = selector(value)
if(available(value)) resolve(value)
})
})
And usage
const user = once(state => state.user)(user => user && user.fullName)
user.then(user => console.log(`User name is ${user.fullName}`)
Or if your data might be changing more than once during application lifecycle you might want to wrap it with something that represent changing data (observable). RX examle
I would like to perform some initial data loading when my first route is rendered (for example, i want to load a list of news articles)
I made a component called News.js which renders the articles. The problem i'm experiencing with the FLUX model is where to load this initial data.
The service i have made to load the data is the following:
import request from 'superagent';
class NewsService {
fetchArticles(callback) {
request
.get('http://localhost/articles')
.accept('json')
.end(function(err, res){
console.log(err);
var result = JSON.parse(res.text);
callback(result);
})
}
}
export default new NewsService ();
This service has to be called somewhere. According to the ReactJS documentation i would perform this operation like this:
export default class News extends React.Component {
constructor() {
super();
this.state = {
_articles: []
}
}
componentDidMount() {
NewsService.fetchProjects(function(articles){
// load articles in the state
this.setState({_articles: _articles})
});
}
render() {
return (
<section>
<h1>Articles</h1>
<ul>
{this.state.articles.map((article) => {
<li>{article.title}</li>
})}
</ul>
</section>
)
}
}
Now my question is, isn't this against the flux principle? Shouldn't the data be called as an Action which then stores it's data in a store such as NewsStore?
Should an action be like the following:
var NewsActions = {
load: function() {
NewsService.fetchProjects(function(articles){
// store the articles in the NewsStore and dispatch afterwards
});
},
create: function(project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_CREATE,
project: project
});
},
update: function(id, project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_UPDATE,
id: id,
project: project
})
},
destroy: function() {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_DESTROY,
id: id
})
}
};
export default NewsActions;
In the Chat-app example of reactjs they provide an API call example. However this API call is called on the application start up (in app.js) which is not applicable in my scenario as i would like to use routings. Would i load the data in the router then? (I am using react-router)
Any feedback regarding this matter or improvements of this code is more than welcome.
EDIT
isn't this against the flux principle?
Maybe, maybe not. Seems like Flux is pretty flexible. From what I understand, it's more of a framework of principles rather than a strict "protocol". It's hard to say, but it appears that both example you've given will work. Like you said, according to the docs, they recommend fetching the data in componentDidMount:
componentDidMount: function() {
$.get(this.props.source, function(result) {
// ...
However, in your example, you're simply moving that API call into a service, which can then interact with the store/dispatcher, etc., in order to be utilized across the entire application.
So what you've done is moved a nice chunk of your application logic to, essentially, its own module (well, a module that is a part of your dispatchers). It appears that it will meet your needs: it can be used across your app, and it can be pulled out or plugged back in as you see fit. I don't see anything wrong with it. Could it be against some principle of Flux? Maybe, maybe not. I doubt it matters, though.
ORIGINAL
I'm not well-versed in Flux architecture, but looking at one of Facebook's examples in their GitHub repo (specifically, Line 24 of TodoMVC):
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Their example doesn't show how TodoStore interacts with the server, but it does look like for their initial state, they're simply querying the todos in the store, and then for changes, their listening for and emitting events.
So as far as getting the initial state, it looks like their example shows querying the store directly. Obviously since the time they made that example and now, there may have been changes, but it may be worth investigating some examples in the Flux repo to get an idea of how it was designed.