To delete an item from a list, I created a new list that does not contain the deleted item, and replaced the old list with the new. Is this the "right" way or should I edit the list in place? I suspect this may be inefficient for JS.
destroy: function(chosenItem) {
var newItems = this.state.items.filter(function(item) {
return chosenItem.id != item.id;
});
this.setState({items:newItems});
}
A couple of things:
if such items have any sort of persistence mechanism attached, consider using any action architecture [see Flux, Reflux...], so that you do not set the state of your component directly, but you delegate deletion to a separate entity, that will later on notify your component of the update;
creators of React evangelise about immutable objects in order to work w/ React, so your choice is definitely fine.
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 was creating a very simple first Native app in React but my app is responding very slow to events such as touch
As suggested I have implemented several things like implementing FlatList instead of mapping and so on..
The two things which i think i might be doing wrong is updating state in
componentDidUpdate() {
var updateCoinData = [...this.props.cryptoLoaded];
socket.on('trades', (tradeMsg) => {
for (let i=0; i<updateCoinData.length; i++) {
if (updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
updateCoinData[i]['mktcap'] = tradeMsg['message']['msg']["mktcap"]
updateCoinData[i]['price'] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(updateCoinData);
}
}
})
or maybe calling setState too often in my child component.
If anyone wants reference this is the link to my repository
https://github.com/irohitb/Crypto
These are the two files where most of the operations are happening
Child Repository
Parent Repository
I know it is probably too much to ask but can someone help me/suggest me how I can improve its performance.
There is a 'structural error', redux abusing. Not directly related to question (peformance) however it can have some indirect relation.
You're processing state/data locally while it's a reducers responsibility.
You should prepare payload from socket data and dispatch redux action/message. Reducer should create copy of prev state (use slice() to copy array, spread operator '...' is slow), search for matching id, update it and return as new state.
Main idea of reducer is extracting logic of state transitions to be simply testable. What can you test when only storing/receiving ready data set?
I cannot find anywhere how to implement this feature that looks so easy to implement.
This feature is mentioned in this dev talk https://youtu.be/-DX3vJiqxm4?t=1741
He mentions that for every object in array he checks upvotes and downvotes to check if list row needs updating, but I just can't implement it.
I have an app in react JS with alot of items, and I am changing only one random item. React of course rerenders the whole list in virtual DOM and diffs the previous and current virtual DOMs of the whole list and it takes long time.
But I would like to avoid rendering the unchanged list items. In my app - if "todo" property hasn't changed, the item doesn't need to be updated.
Here is a demo of my app: https://jsfiddle.net/2Lk1hr6v/29/
shouldComponentUpdate:function(nextProps, nextState){
return this.props.todo!==nextProps.todo;
},
I am using this method in the list item component, but the this.props.todo is the same as nextProps.todo so no rows are updated when I change a random item of the first five items.
It's because you haven't updated the reference of the Todo list array this.props.todos.
changeOne:function(){
var permTodos = this.props.todos.concat([]);
permTodos[Math.floor((Math.random() * 5) + 0)]= {todo:((Math.random() * 50) + 1)};
this.setState({ todos: permTodos });
},
This is why immutability is important.
If you think this is getting too complex. Use libraries such as immutableJS which does that automagically for you.
Edit: Working fiddle link!
https://jsfiddle.net/11z2zzq6/1/
I'm trying to follow the "Observable data store" pattern in Angular 2 (detailed in this blog post from Angular University) From what I understand, this means that, if I have a service called TodoStore, I would subscribe to TodoStore.items$ so I could get all the latest updates for my to-do list in real time (as other components add, remove, or edit Todos.
However, what if I have two components side-by-side that display different Todolists, filtered on the server-side? One might display Todos due today while another would show Todos due on a user-selected date.
In this case, I would not be able to use the same TodoStore, as both would be fetching different data from the server. How do I handle sharing this service between these two components? My urge is to go back to an angular1-style getToDoList() service method, but then I'm storing state inside a single component and can't possibly share data between multiple components without making extra API calls.
If your lists really have to be filtered server-side and you have an unknown number of simultaneously displayed lists and a new server-request has to me made for each list + filter, then it is perfectly possible that using a single observable (TodoStore.items$) might not be a viable solution here and maybe some kind of getTodoList(forFilter) might be easier/quicker to implement.
Remeber: There is no such thing as "The perfect solution for all cases."
However: Even in this case you could use a store, which could something like this:
interface ITodoListDictionary {
[key: string]: Todo[];
}
#Injectable()
export class TodoStore {
todoLists$: BehaviorSubject<ITodoListDictionary> = new BehaviorSubject<ITodoListDictionary>({});
getListByKey(key: string): Observable<Todo[]> {
return this.todoLists$
.pluck(key)
.distinctUntilChanged() // optional, so you only get the update if there is an actually new list
.share();
}
// this would be called whenever the rest-response for a given list arrives
setListByKey(key: string, list: Todo[]): void {
this.todoLists$.take(1)
.do(dict => {
const newDict = Object.assign({}, dict, {[key]: list});
// if you are using TS2.1 you could also use:
// const newDict = {...dict, {[key]: list}};
this.todoLists$.next(newDict);
})
.subscribe();
}
}
...and in your template you could use it like this
<todo *ngFor="let todo of todoStore.getListByKey(someKey) | async" todo="todo"></todo>
Please keep in mind that is just one possible solution out of many - without seeing your actual application-flow it is hard to tell which might be the best solition.
I am in the early development stage of a React+Redux game and have followed Redux best practices: pure reducer, presentational/container component separation, using getState() only in Reducer (as opposed to in action creator) etc. The app seems to be working as expected but when I try to reverse an action using Time Travel, even though the state property map[][] and it's computed connected component prop change as expected, the result doesn't get reflected on the UI properly (specifically the player position on the map doesn't follow what state dictates). When I inspect the state changes I can see that all necessary changes are correctly taking place between different states. Here is my reducer:
const gridReducer = (state, action) => {
if (typeof state === 'undefined'){
let dungeon = new Dungeon();
dungeon.generate();
return {
boardWidth: Math.floor(((70/100) * window.innerWidth) / 20),
boardHeight: Math.floor(((70/100) * window.innerHeight) / 20),
map: dungeon.map,
position: dungeon.playerPosition
}
}
switch (action.type) {
case 'GRID_RESIZE':
return {...state,
boardWidth: action.newBoardWidth,
boardHeight: action.newBoardHeight
}
//This is where I have the issue, map correctly changes both when interacting with the game and when reversing using time travel however the UI fails to update (only in reverse)!
case 'MOVE':
let dungeonObj = new Dungeon(state.map.slice(), {...state.position});
if (dungeonObj.movePlayer(action.direction)) {
return {...state,
position: dungeonObj.playerPosition,
map: dungeonObj.map
}
} else return state;
default:
return state;
}
}
Here is the complete code if you want to take a look! The app currently only supports moving the player in the dungeon by pressing arrow keys and the view is supposed to always be centeral based on the position of the player (player fails to move back when using time travel)
http://s.codepen.io/sabahang/debug/GjrPNQ
PS: Dungeon.generate does use Math.Random but I'm only using this function in initialState and for dispatched actions I'm only making a shallow copy of the generated map by sending the current state to Dungeon constructor and use its other methods (eg. movePlayer)
Found the culprit. It's not Redux's fault at all, it's about the way React works! If you are new to React and you haven't fallen into this trap yet wait for it!
It has to do with the fact that most of the conventional ways of copying a deeply nested object which is needed in Redux to implement a pure Reducer is in fact making a shallow copy of the objects and properties' memory references are still pointing to the original State. React updates the UI based on a deep comparison of the old state and the new one and when some of the references are the same it fails to update the UI properly. Here I have a 2 dimensional array map[][] which is an object and although I'm using ES6 spread operator to avoid modifying the original state because a shadow copy is being made, deeply nested indexes of the original map[][] are being modified. One solution would be to use `Array.map()' to create a completely new object but I ended up using immutablejs and it fixed my problem with the time travel slider.
This is a highly recommended reference if you don't want to spend weeks chasing similar bugs in complicated apps: http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html
and there are tons of immutability helpers to help based on your specific need:
https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities
This one also looks interesting for Redux only:
https://github.com/indexiatech/redux-immutablejs
This question is potentially a duplicate of the following:
React-redux store updates but React does not