I'm working on one of my first react projects and got this question.
What is the best way to handle and store data in react with using this.state?
My current workflow:
fetch data from api in componentDidMount()
storing the data in this.state (for example in this.state.account_data as child object`
using the data in value attributes of controls like <input> in this way: this.state.account_data.username
each control has his own onChange-handler onChange={e => this.onChangeUsername(e)}
The method onChangeUsername(e) changes this.state with this.setState({username: e.target.value}).
So, the problem is: After changing the unsername control value, I have two variants of the username in this.state: this.state.account_data.username and this.state.username. That requires to create another part of code to update this.state.account_data, because I want to send the new data to the API by UserService.update(this.state.account_data).
Is there an easier way or what is the best practice?
According to official documentation, React suggests to use this library to handle data mutations: immutability-helper.
The idea is that in your onChangeUsername(e) you will copy the original part of state you need to update with update function. In this case const myNewAccountData = update( this.state.account_data, ... ) and then you set the new state with this.setState({account_data : myNewAccountData }).
See this answer for an example on updating nested object with immutability-helper.
And Here you can find an explanation on why to avoid nested object as React state or at least why it may be considered not a best practice.
Related
I've just started using Recoil on a new project and I'm not sure if there is a better way to accomplish this.
My app is an interface to basically edit a JSON file containing an array of objects. It reads the file in, groups the objects based on a specific property into tabs, and then a user can navigate the tabs, see the few hundred values per tab, make changes and then save the changes.
I'm using recoil because it allows me to access the state of each input from anywhere in my app, which makes saving much easier - in theory...
In order to generate State for each object in the JSON file, I've created an component that returns null and I map over the initial array, create the component, which creates Recoil state using an AtomFamily, and then also saves the ID to another piece of Recoil state so I can keep a list of everything.
Question 1 Is these a better way to do this? The null component doesn't feel right, but storing the whole array in a single piece of state causes a re-render of everything on every keypress.
To Save the data, I have a button which calls a function. That function just needs to get the ID's, loop through them, get the state of each one, and push them into an Array. I've done this with a Selector too, but the issue is that I can't call getRecoilValue from a function because of the Rules of Hooks - but if I make the value available to the parent component, it again slows everything right down.
Question 2 I'm pretty sure I'm missing the right way to think about storing state and using hooks, but I haven't found any samples for this particular use case - needing to generate the state up front, and then accessing it all again on Save. Any guidance?
Question 1
Get accustomed to null-rendering components, you almost can't avoid them with Recoil and, more in general, this hooks-first React world 😉
About the useRecoilValue inside a function: you're right, you should leverage useRecoilCallback for that kind of task. With useRecoilCallback you have a central point where you can get and set whatever you want at once. Take a look at this working CodeSandbox where I tried to replicate (the most minimal way) your use-case. The SaveData component (a dedicated component is not necessary, you could just expose the Recoil callback without creating an ad-hoc component) is the following
const SaveData = () => {
const saveData = useRecoilCallback(({ snapshot }) => async () => {
const ids = await snapshot.getPromise(carIds);
for (const carId of ids) {
const car = await snapshot.getPromise(cars(carId));
const carIndex = db.findIndex(({ id }) => id === carId);
db[carIndex] = car;
}
console.log("Data saved, new `db` is");
console.log(JSON.stringify(db, null, 2));
});
return <button onClick={saveData}>Save data</button>;
};
as you can see:
it retrieves all the ids through const ids = await snapshot.getPromise(carIds);
it uses the ids to retrieve all the cars from the atom family const car = await snapshot.getPromise(cars(carId));
All of that in a central point, without hooks and without subscribing the component to atoms updates.
Question 2
There are a few approaches for your use case:
creating empty atoms when the app starts, updating them, and saving them in the end. It's what my CodeSandbox does
doing the same but initializing the atoms through RecoilRoot' initialState prop
being updated by Recoil about every atom change. This is possible with useRecoilTransactionObserver but please, note that it's currently marked as unstable. A new way to do the same will be available soon (I guess) but at the moment it's the only solution
The latter is the "smarter" approach but it really depends on your use case, it's up to you to think if you really want to update the JSON at every atom' update 😉
I hope it helps, let me know if I missed something 😊
I am new to both React-JS and Oboe.js. I am trying to speed up loading of some JSON data by using Oboe to stream the results. Unfortunately I am unable to do an update state in the function block. So I try to call another function that does the stateSet. Below is a method I have tried but doesn't work. It errors out a mapping function that uses search-results to render it in a table.
var that = this;
oboe({
url: //url,
method: 'POST', // optional
body: //POST-DATA, // optional
})
.on('node', '*', function(things){
that.updateState(things);
// This callback will be called everytime a new object is
// found in the foods array.
console.log( 'Go eat some', things.id);
});
updateState = (props) => {
this.setState({search-result: props});
}
What I am not sure about is the right way of updating a state with oboe.js and React?
Is there a better library to use for streaming JSON data into React?
Recommended approach
If you have the ability to change things server-side, then I would not recommend using Oboe for this. Oboe is useful if your only alternative is to load a large JSON object and you would like to access that data before the whole thing can be parsed.
The best way to optimize loading a lot of data on a client is to send less data at a time and to make multiple requests. A web-socket is the best approach, and Socket.io is a good tool for doing that.
If you need to use Oboe
I'm working to put together an example of oboe.js + react for you to look at, though it's tricky as much of the activity of Oboe happens outside the React lifecyle. I'll update this answer with that example 👍
i just want to know this.whether state json object binding and normal json object binding is same or not? below is the example.
1st example
state = { name: "Default", email: "" };
binding data : this.state.name
this.setState({ name: e.currentTarget.value })
2nd example
const data= {name: "Default", email: ""}
binding data to control: data.name
onchange={e=>data.name=e.value}
both are working fine but want to know which one is better in performance?
my application dosent need any imutable data because i not displaying data dynamically i need to fetch the data from api on component load and posting the data to api on form submit. so i am using the 2nd approch. where i feel state will unnecessarly load render object.
so can any one suggest which one is better?
If the state is not associated with any UI component then changing it will not re-render anything so functionally both will work the same.
In terms of performance 2nd approach will be faster as it is a direct object manipulation whereas calling setState is a function call and doesn't guarantee immediate execution.
However do not use 2nd approach at all because it will create confusion for the next developer who manages such code(In my opinion). In the long run when the data grows you will have to keep a separate state obj for managing the data and the UI.
So it is always better to keep them separate from the beginning.
So here's my situation. I have a component that loads some data from the database, like so:
const task= fetch(`http://example.com`)
.then(result => result.json())
.then(result => {
this.setState({
items: result
})
});
addTask(task);
Now, if I add this code to the componentWillMount method, it does work however, every time I load the component, this method will fire and I don't want it calling the database every time. I want it called once on first load and then stored in the state and never called again.
And because I am rendering a list of items, I have to manually set this items variable in the constructor to avoid null errors:
constructor() {
super();
this.state = {
items: [],
}
}
My question is, how can I load this data only once and then set it to the items state variable. I have tried adding all this code to the reducer of the component (I am using redux), but it doesn't work.
There are several ways of achieving what you are trying to do, but every each of them brings up more questions to answer I think.
First question is, lets say you stored your data locally after your first fetch, whats gonna happen if the data on the server changes? You can somehow notify the client side that data is change and then fetch the data again to sync local data. Or maybe you can periodically check for the new data. There are lots of ways to go.
This situation is exists if you load the data on a parent component and pass the data to the component that is gonna use it or use persistent redux or similar solution.
You can go with a 3 library solution like Realm, Firebase or similar. These systems have persistent or local storage options that will reduce the download when you make call to data. But these solutions bring up the need for changing your back-end logic.
Like I said before there is no single way to handle these situations and it really depends on your project and preference.
I am working on an app using React with Alt flux implementation.
Currently, I'm trying to figure out the best way to use data from multiple Alt stores to compose a request to the backend.
Say, I have a SheetDataStore, FiltersStore and a fetchFilteredData method in my DataSource file. To get proper data from backend, i need to pass to fetchFilteredData both some sheet data and filters, but when you call the fetchFilteredData method from one of the stores, you can pass some arguments and the state of just that exact store.
So i see 2 ways of handling this case:
1) I make 2 actions: prepareFilters and fetchData. The prepareFilters is called from a component and gets handled by FilterStore, which prepares all required data and after that calls fetchData action, passing the prepared data as an argument. The SheetDataStore handles the fetchData action and calls the fetchFilteredData having all required data now.
What i don't like here is that it seems to me, Stores should not be calling actions, so that's kind of a hacky solution.
2) I import FilterStore to the SheetDataStore and use FilterStore.getState() inside of one of SheetDataStore methods to get all the data i need. That seems easy, but there might be some pitfalls of coupling the stores like that.
Are there any best practices for such a case? Maybe some of you faced similar issue and can recommend which of the paths to take from your experience?
Do the binding in the component. Say you have FilterComponent then pass the search action SheetDataAction.search to it:
<FilterComponent search={SheetDataAction.search} />
And in the FilterComponent.render() do something like <button onClick={() => this.props.search(this.props.criteria)} />