I'm constructing a React App that is basically a photo sharing app.
Here's some use cases:
User can upload photos and videos
User can see the photos in a list view
User can reorder the photos in the list
User can select photos from list and performs actions based on their selection
Users can attribute properties to these photos, such as message, title, etc. Lets call an image + its properties a Post
Here's some major architectural components:
A CDN service to upload, host, and transform image and video creation
A backend application paired with a DB for persistent storage
I'm looking for a good way to organize all this data in state. One thought I had was to break up the state into separate, simple data structures.
Roughly this:
posts <Array> maps Post index to Post ID
media <Object> maps Post ID to Image Urls
selectedPosts <Object> maps Post ID to Boolean
loadingPosts <Object> maps Post ID to Boolean
So here we have four data structures.
posts: Determines what posts are in state and in what order
media: Attributes Post IDs to Image URLs
selectedPosts: Determines what posts are selected
loadingPosts: Determines if a given post is loading or not
I'm surfacing these via four React Contexts
Breaking up state into separate contexts makes it really easy for dependent components to subscribe to exactly the state they need. For example:
import React from 'react'
import useMedia from '/hooks/media'
export default ({ postId }) => {
const { media } = useMedia() // useMedia uses useContext under the hood
const imageForThisPost = media[postId]
return (
<Image src={imageForThisPost}/>
)
}
What I really like about this is that this component gets exactly the state it needs from global state and really only has one reason to re-render (pretend i'm using useMemo or something). I've worked with some tough React Redux web apps in the past where every component re-rendered on any state change because all the state was in one data structure (albeit memoized selectors could have fixed this).
Problems arise when it comes to use cases that impact multiple contexts. Take uploading an image as an example:
The sequence of events to upload a photo looks like this:
An empty post with ID, "ABC", is selected. Update selectedPosts context
User uploads a file and we wait for CDN to return image url
Update loading context of post ABC (loading == true)
(receives image url)
Update posts context at ABC
Update media context at ABC
Update loading context of post ABC (loading == false)
Deselect post ABC. Update selectedPosts context
Long, intricate, async sequences like this are tough to deal with, encapsulate, reuse, and test.
What's a better way to organize state for medium sized web applications like this with potentially long sequences of async actions and somewhat complex state?
Wishlist:
Easy to control re-renders
Easy to add extend/change app functionality (not a fan of huge deeply nested data structures)
Easy to test
Does not use Redux (but useReducer is fine) (I just don't like the huge overhead that comes with redux)
Anyone have any thoughts?
I know one way might be to emulate Redux using useReducer, actions, and selectors. And thankfully dispatch is a stable function identity in React. Idk, I just really don't like dealing with big, deeply nested objects. When product requirements change, those are such a pain to deal with because the entire application depends on a particular schema shape.
Old post, but am assuming that selectedPosts and loadingPosts are filtered posts objects.
Personally I would probably not have them as separate, but filter of posts which are marked loading / selected and upload those in code via some action?
I.e. state has list of posts, with loading / selected props and code does the filter on posts to upload - are you over complicating it? Presuming with some lookup one to many / many to many? on state for media to create the association? What did you end up doing? I would be using useReducer and dispatching updates to state, not sure context is needed unless its deeply nested.
I'm working on a React application that is connected to a few ASP.NET Core WebAPI microservices. Each of these services have different entities that are used throughout the application.
Within the complete application, there are enums and 'configurational data' that can be configured.
Imagine configurational data as just simple tables, with two fields (Id and Value).
Different entities have FK relationships to the configurational data, and/or have enum fields. I'm trying to understand how I would, in a performant way, can load the configurational data and all the used enums upfront upon page load, so that these can be used in dropdowns. I'm pretty new to React (1 month), so still learning day by day.
I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url, to fetch the possible values for a certain table or enum, but it's very impractical and will prove to be not so performant once there are 1000 users using the application, and all doing calls to these api's multiple times, just for some dropdowns.
So, what is the advised approach to have some sort of splash screen in React AND call APIs to cache values, that then can be used in other components?
"I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url"
You should not do this :)
Before I suggest a solution I want to go through a couple of important key concepts.
Firstly
The render method will always run once before you async stuff happens (like your GET).
Lifecycle methods order which will trigger the First Render : constructor => componentWillMount => render => componentDidMount.
This means that you will have to have all your data ready for render initially. Or have conditions which prevents certain jsx for being called.
Secondly
If you have dynamic content, which will be the options in your dropdown, you'll have to get it from somewhere. If it's static you can define a list locally.
If you want to save the response you could use localStorage or if you are using redux; the middleware redux-persist to persist the store.
I personally don't see the purpose though, because if the dynamic options updates you would want that to update the application state. And even 1000 simple calls like that is not expensive for the server.
If you are using redux, you should keep the options there, because then you won't have to make an GET every time you're mounting the component with the dropdown.
Suggestion:
Many ways you can do this but here is a simple solution).
keep a local state in component and initialize it for first render
this.state = {dropDownOptions: []}
Then in componentDidMount make api call:
fetch(url).then((response)=>this.setState({dropDownOptions: response}));
And lastly in your render method:
<MyDropDown options={this.state.dropDownOptions} .../>
Im a little uncertain as to how Redux ties in with React ( without using the ReactRedux library ). Assume the following component structure
App
--TodoListController
----SomeComponent1
----TodoList
------TodoItem
--ProfileController
Question 1. Which components should listen for changes?:
Im assuming that the proper component to subscribe for state changes in the redux main (and only ) store should be the TodoListController and the ProfileController respectively ( essentially the non presentation components ).
Is this correct to assume or should all components listen to the state and render whatever is of interest to them? I essentially dont know which component should listen to state changes and am only guessing at this point
Question 2. Handling network calls:
I know this is to be examined per case but ill mention it anyway. Currently im handling network calls in the following manner:
A) When TodoListController mounts i get the state from the mainstore and also initiate a request to the server for the latest data. I also listen for changes in the store. So in practice:
class TodoListController extends React.Component{
componentWillMount(){
mainStore.subscribe()
getDataFromServer(function(data){
mainStore.dispatch(data)
})
}
getDataFromStoreAndUpdate(){
this.state.datawecarefor = mainStore.todoReducer.data
//set the state here to trigger a rerender
}
componentWillUnmount(){
mainStore.unsubscribe()
}
render(){
//render whatever component here that uses this.state.datawecarefor
}
}
Do you see any obvious flaws with this approach? I dont know what i dont know at this point.
Question 3. Where should store related helper functions live?
I currently have a reducer that holds all todolists for various users. Currently, when the redux store state updates i retrieve all this data and iterate through it to find the user im interested in. This shouldnt be in the controller itself but as a hepler function. I thought of creating a wrapper around the Redux store and it's reducers to create functions like getTodoListForUser(userId) but i dont know if thats the right approach. How do you handle logic like that?
P.S: Many people will point out that i should use the ReactRedux library that comes with many optimisations and they re probably right. This however isnt a production project, only one im trying to put together to better udnerstanding the core of both these two libraries before moving to something more optimal.
I know you don't want to use ReactRedux, but luckily enough there is a video of Dan Abramov explaining the source code. You should watch it, it will explain why they did what they did and how they did it. When I was first learning how redux and react worked together it made every so much more clear (and then I used ReactRedux anyway :)).
https://www.youtube.com/watch?v=VJ38wSFbM3A
There has been a lot of debate on where to connect React App's to the redux store. But it's mostly recommended that you want to connect where it makes logical sense. For example, if you have a container component that holds a bunch of comments, you don't need to connect all of the comments, you can just connect the container. In the same light you don't just want to connect your entire app at the top because then its more expensive to diff and update your app.
On another note you should probably try to handle network calls in redux middleware and dispatch an action your react component catches to cause a render.
I'm currently learning React and I am trying to figure out how to use it with Redux for building a mobile app. I'm kind of confused on how the two are related/usable together. For example, I completed this tutorial in React https://www.raywenderlich.com/99473/introducing-react-native-building-apps-javascript, but now I want to play around with adding some reducers/actions to that app and I am not sure where those would tie in with what I've already done.
React is a UI framework that takes care of updating the UI in response to the “source of truth” that is usually described as a state “owned” by some component. Thinking in React describes the React state ownership concept very well, and I strongly suggest you go through it.
This state ownership model works well when the state is hierarchical and more or less matches the component structure. This way the state gets “spread out” across many components, and the app is easy to understand.
However sometimes distant parts of the app want to have access to the same state, for example, if you cache fetched data and want to consistently update it everywhere at the same time. In this case, if you follow the React model, you’ll end up with a bunch of very large components at the top of the component tree that pass a myriad of props down through some intermediate components that don’t use them, just to reach a few leaf components that actually care about that data.
When you find yourself in this situation, you can (but don’t have to) use Redux to “extract” this state management logic from the top-level components into separate functions called “reducers”, and “connect” the leaf components that care about that state directly to it instead of passing the props through the whole app. If you don’t have this problem yet, you probably don’t need Redux.
Finally, note that Redux is not a definitive solution to this problem. There are many other ways to manage your local state outside the React components—for example, some people who didn’t like Redux are happy with MobX. I would suggest you to first get a firm understanding of React state model, and then evaluate different solutions independently, and build small apps with them to get a sense of their strengths and weaknesses.
(This answer is inspired by Pete Hunt’s react-howto guide, I suggest you to read it as well.)
I've found that the ideal path for adding Redux to an application/stack is to wait until after you/app/team are feeling the pains that it solves. Once you start seeing long chains of props building up and being passed down through multiple levels of components or your finding yourself orchestrating complex state manipulations/reads, that could be a sign that your app may benefit from introducing Redux et al.
I recommend taking an app that you've already built with "just React" and see how Redux might fit into it. See if you can gracefully introduce it by plucking out one piece of state or set of "actions" at a time. Refactor towards it, without getting hung up on a big bang rewrite of your app. If you're still having trouble seeing where it might add value, then that could be a sign that your app is either not large or complex enough to merit something like Redux on top of React.
If you haven't come across it yet, Dan (answered above) has a great short-video series that walks through Redux on a more fundamental level. I highly suggest spending some time absorbing pieces of it: https://egghead.io/series/getting-started-with-redux
Redux also has some pretty great docs. Especially explaining a lot of the "why" such as http://redux.js.org/docs/introduction/ThreePrinciples.html
I have prepared this document to understand Redux. Hope this clears your doubt.
-------------------------- REDUX TUTORIAL ----------------------
ACTIONS-
Actions are payloads of information that send data from your application to the store. They are the only source of information from the store. You can send them
only using store.dispatch().
Example-
const ADD_TODO = 'ADD_TODO'
{
type:ADD_TODO,
text: 'Build my first redux app'
}
Actions are plain javascript object. Action must have a [ type ] property that indicates the type of action being performed. The type should be defined as a string constant.
ACTION CREAToRS-----
---------------- ---- Action creators are exactly the function that creates action
It is easy to conflate the terms - action and action creator.
In redux action, creator returns an action.
function addToDo(text) {
return {
type: ADD_TODO,
text
}
}
to initialte dispatch pass the result to the dispatch() function.
dispatch(addToDo(text));
dispatch(completeToDo(index))
Alternatively, you can create a bound action creator that automatically dispatches.
cosnt boundAddTodO = text => dispatch(addToDo(text));
now you can directly called it
boundaddTodO(text);
The dispatch() functionn can be directly accessed from store.dispatch(). but we
access it using a helper connect() method.
Actions.js.....................
Actions...........
exports cosnt ADD_TODO = 'ADD_TODO';
exports cosnt TOGGLE_TODO = 'TOGGLE_TODO'
Actions Creators
export function addToDO(text){
return {
type: ADD_TODO,
text
}
}
.........................REDUCERS..................................
Reducers specify how the applications state changes in response to actions sent to the store.
Designing the state shap
In redux all the application state is store in single object. You have to store some data as well as some state.
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using redux',
completed: true
},
{
text: 'Kepp all the state in single tree'
}
]
}
Handling Actions
---------------- the reducers are the pure functions that take the previous state and action, and return a new state.
(previousState, action) => newState
We will start by specifying the initial state. Redux will call our reducers with an undefined state for the first time. this is our chance to return the state of our app.
import { visiblilityFilters } from './actions';
const initialState = {
visibilityFilter: VisibilityFilters.SHOW_ALL,
todo: []
}
function todoApp(state, action){
if(typeof state == 'undefined'){
return initialState;
}
// dont handle other cases as of now.
return state;
}
you can do the same using ES6 way of handling the JS
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
................................. STORE...................................
The store is a object that brings them together. the store has following responsbility
hold application state
allow access to state via getState()
Allow state to be updated via dispatch()
Register listerneres via suscriber(listener)
Note. use combineReducers() to combine several reducers into one.
const store = createStore(todoapp); // the todoapp are the reducers
This is how redux works. A action is dispatched from any compoenent or view. Action MUST have "type" property and may be any property which holds information of action happened. The data passed in action, could be relevant to different reducer, so same object get passed to different reducer. Each reducer takes/ make-out its part/contribution to state. The output is then merged and new state get formed, and the component which must be subscribed for state change event gets notified.
In above example, brown color has all 3 component RGB. Each reducer receives same brown color and they seperate out its contribution to the color.
Firstly, you don't need to add Redux to your application if you don't need it! Simple, so don't force yourself to include it in your project if you don't need it at all! But that doesn't mean Redux is no good, it's really helpful in large applications, so read on ...
Redux is a state management for your React application, think about Redux like a local store which track of your state as you go, you can access the state in any page and route you want, also compare to Flux, you only have one store, means one source of truth...
Look at this image to understand what Redux does first at a glance:
Also this's how Redux introduce itself:
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in
different environments (client, server, and native), and are easy to
test. On top of that, it provides a great developer experience, such
as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library.
It is tiny (2kB, including dependencies).
Also as per documentation, there are Three Principles for Redux as below:
1. Single source of truth
2. State is read-only
3. Changes are made with pure functions
So basically when you need to a single store to keep track of anything you like in your application, then Redux is handy, you can access it anywhere in your app, in any route... simply using store.getState();
Also using the middleware Redux, you can do manage the state much better, there a list of handy components and middleware on official page of Redux!
Simply if your application gonna be big, with many components, states and routing try to implements Redux from start! It will help you on the way for sure!
When we write application we need to manage state of the application.
The React manages states locally within the component if we need to share the states between components we can use props or callbacks.
But as application grows it becomes difficult to manage states and state transformations.State and state transformations need to properly tracked in order to debug the applications.
Redux is a predictable state container for JavaScript apps that manages state and state transformations and is often used with React,
The concept of redux can be explained in following image.
When user triggers an action when user interact with the component and an action is dispatched to store then the reducer in the store accepts the action and update the state of the application and stored in the application wide immutable global variable when there is an update in store the corresponding view component subscribed to the state will get updated.
Since state is managed globally and with redux it is easier to maintain.
Having used Redux and personally finding it cumbersome, I found passing around an object to my components as a prop can be a much easier way to maintain state. Not to mention it's an easy way of making references to functions to call in other components. It can solve a lot of the cumbersome issues of passing messages between components in React, so it's a two for one.
Maybe at official flux website I saw a video were mentor said something like:
Only top-level React views should know about stores. All not top level
views should be dump and receive all information as properties.
Question: Is that right? Your argumentation, please
BUT, suppose you have some small React view Button.react that's reused on multiple pages. And suppose Button.react must know about some store data. If we won't fetch all data directly from the Button.react, we get a duplication of code at each top-level component which reuse Button.react. Is that ok for you?
I hope I am understanding your question.
One of the characteristics of React is its one-way data flow. Each component can be used by another component, just like one function can call another function. Just like a function, a React component should typically be able to get all the info it needs to do work (render itself) from the arguments passed into it. This is the function of props in React. When using Flux, sometimes the React Components, which are typically near the top of the view hierarchy, that actually fetch the data from the stores to pass down thru the application are called Controller-Views.
It is not an enforceable rule that every component doesn't become a Controller-View, getting its own state directly from a store, but it is a general practice for good reason. consider the two functions:
function renderToggleButton( isSelected ){
//... render the button
}
vs
function renderToggleButton(){
var isSelected = StateStore.getButtonSelectedState( id );
//... render the button
}
I think you will agree that the second function is more complicated and difficult to test. It has to know from where it is getting it's initial conditions. It also has to know how to identify itself in the context of the application. These are two things the function should not have to know.
Now imagine an application full of functions like this. If one function is misbehaving, it becomes very difficult to trace its inputs; to test it under controlled conditions. I hope that clarifies the guidance given for passing data thru the application as props.