Redux --I want to render SimpleModal component in handleClick , how can I achieve it through redux - redux

I want to render SimpleModal component in handleClick , how can I achieve it through redux
can I do this way??
//ReactDOM.render(, document.getElementById("123"));
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import Redux,{createStore,combineReducers } from 'redux';
import SimpleModal from './modal.js';
import {Provider, connect} from 'react-redux';
import {displayItems} from './reducers.js';
const ecommerceAppReducer = require('./reducers.js').default;
const store = createStore(ecommerceAppReducer);
const EcommerceApp = React.createClass({
componentDidMount(){
store.dispatch({
type: 'LIST_DATA',
id: 12
});
},
handleClick: function(entity){
this.props.dispatch({
type: 'DISPLAY_INFORMATION',
entity:entity
});
**Want to render a SimpleModal here**
},
render() {
return (
<div>
<ul>{
this.props.state.displayItems.map(function(e) {
return <li><a onClick={this.handleClick.bind(this,e) }>{e.name}</a></li>
}.bind(this))
}
</ul>
</div>
);
}
});
const mapStateToProps = function (state) {
return {state};
}
const Eapp = connect(mapStateToProps)(EcommerceApp);
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Eapp />
</Provider>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));

In React, your render function should return what the app looks like currently. As in right now. React will take care of updating and rendering and so forth as long as you use one of the methods to inform React when it needs to rerender something.
One method is to call React.render on the root of your app. This is the worst way, but not terrible for small apps. Only recommended if you know what you're doing and even then there are probably better methods.
The next is to use setState() in your component. React will call that particular component's render method sometime after that. It's much more precise in that not your entire app gets rerendered (although you can always stop the rendering cascade by implementing shouldComponentUpdate judiciously).
Next is to call forceUpdate which is terrible to use unless you are really sure of what it is you're getting yourself into. React-Redux uses this because they do know what they're getting into.
And finally, there's React-Redux, which isn't really another way for React to render your component. But it is a new way for the developer. This is by far the recommended way to do things.
To use it, you just follow the connect prescribed method for transforming your Redux state into props for your component.
This requires reading the a Redux docs. It's a long and arduous process that is guaranteed to make anyone a better developer.
In your mapStateToProps implementation it's important to be very selective with what parts of the state you pass along to your component.
Don't just go and pass the entire Redux state. This would cause your entire app to rerender if anything at all changed anywhere in your app. Less than optimal. Only pass what you need. Don't even pass what child components need. They get their own connect.
Now onwards and forwards we go.
You want handleClick to pop up some stuffs and show it to the user.
Method 1: Use alert. It's ugly and super simple. It provides a terrible user experience so it's not recommended.
Method 2: Use React-Redux. dispatch an action that causes your reducer to put some data in the state that lets your app know to show the data. Looks like you are already doing that!
Redux will then inform React-Redux that something has changed.
React-Redux will then check if any of your components use the information in the state that was just changed. It knows what you use because this is what you returned from your mapStateToProps function.
React-Redux will then tell React to rerender any of the components that it finds need updating.
When your component's render method gets called, you'll get the new info in the props. So do:
render() {
return (
<div>
{Boolean(this.props.modalOpen) && <MyConnectedModal />}
<ul>{
this.props.displayItems.map(function(e) {
return <li key={e.name}><a onClick={this.handleClick.bind(this, e) }>{e.name}</a></li>
}.bind(this))
}
</ul>
</div>
);
}
There's still plenty wrong with the above code. You should, for instance, never bind in render.
Note that the modal is a component apart. It gets its data from React-Redux and not from props passed by the parent. This means your EcommerceApp component does not have to be responsible for updating the modal if any data it's displaying changes. Redux will take care of that. Actually with React-Redux's help of course. And React, naturally. Not necessarily in that order.
To recap what's going on here: Your render method tells React not what to pop up, but what the final result should look like. This is an enormous difference and pretty much the entire point of React.
You never tell React what changed. You always tell it what the final result should look like. React will then go and figure out what happened and will find an efficient way to show it in your browser window or electron or nw.js desktop app or native mobile app or anywhere else React worx.

Related

Prevent React Native remount of component on context value change

In a chat app I am building I want to deduct credits from a user's account, whenever the users sends a message and when a chat is initiated.
The user account is accessible in the app as a context and uses a snapshot listener on a firestore document to update whenever something changes in the user account document. (See code samples 1. and 2. at the bottom)
Now whenever anything in the userAccount object changes, all of the context providers children (NavigationStructure and all its subcomponents) are re-rendered as per React's documentation.
This, however causes huge problems on the chat screen that also uses this context:
The states that are defined there get re-initalized whenever something in the context changes. For example, I have a flag that indicates whether a modal is visible, default value is visible. When I go onto the chat screen, hide the modal, change a value manually in the firestore database (e.g. deduct credits) the chat screen is rerendered and the modal is visible again. (See code sample 3.)
I am very lost what the best way to solve this issue is, any ideas?
Solutions that I have thought about:
Move the credits counter to a different firestore document and deduct the credits once per day, but that feels like a weird workaround.
From Googling it seems to be possible to do something with useCallback or React.memo, but I am very unsure how.
Give up and become a wood worker...seems like running away from the problem though.
Maybe it has something to the nested react-navigation stack and tab navigators I'm using within NavigationStructure?
Desperate things I have tried:
Wrap all sub-components of NavigationStructrue in "React.memo(..)"
Make sure I don't define a component within another component's body.
Look at loads of stack overflow posts and try to fix things, none have worked.
Code Samples
App setup with context
function App() {
const userData = useUserData();
...
return (
<>
<UserContext.Provider value={{ ...userData }}>
<NavigationStructure />
</UserContext.Provider>
</>
}
useUserData Hook with firestore snapshot listener
export const useUserData = () => {
const [user, loading] = useAuthState(authFB);
const [userAccount, setUserAccount] = useState<userAccount | null>();
const [userLoading, setUserLoading] = useState(true);
useEffect(() => {
...
unsubscribe = onSnapshot(
doc(getFirestore(), firebaseCollection.userAccount, user.uid),
(doc) => {
if (doc.exists()) {
const data = doc.data() as userAccount & firebaseRequirement;
//STACK OVERFLOW COMMENT: data CONTAINES 'credits' FIELD
...
setUserAccount(data);
}
...
}
);
}, [user, loading]);
...
return {
user,
userAccount,
userLoading: userLoading || loading,
};
};
Code Sample: Chat screen with modal
export const Chat = ({ route, navigation }: ChatScreenProps): JSX.Element => {
const ctx = useContext(UserContext);
const userAccount = ctx.userAccount as userAccount;
...
//modal visibility
const [modalVisible, setModalVisible] = useState(true);
// STACK OVERFLOW COMMENT: ISSUE IS HERE.
// FOR SOME REASON THIS STATE GET'S RE-INITALIZED (AS true) WHENEVER
// SOMETHING IN THE userAccount CHANGES.
...
return (
<>
...
<Modal
title={t(tPrefix, 'tasklistModal.title')}
visible={ModalVisible}
onClose={() => setModalVisible(false)}
footer={
...
}
>
....
</Modal>
...
</>)
}
Any change to the context does indeed rerender all consumer components whether they use the changed property or not.
But it will not unmount and mount the component which is the reason why your local state gets initialized to the default value.
So the problem is not the in the rerenders (rarely the case) but rather <Chat ... /> or one of it's parent component unmounting due to changes in the context.
It is hard to tell from the partial code examples given but I would suggest looking at how you use loading. Something like loading ? <div>loading..</div> : <Chat ... /> would cause this behaviour.
As an example here is a codesandbox which illustrates the points made.
This is a characteristic of React Context - any change in value to a context results in a re-render in all of the context's consumers. This is briefly touched on in the Caveats section in their docs, but is expanded on in third-party blogs like this one: How to destroy your app's performance with React Context.
You've already tried the author's suggestion of memoization. Memoizing your components won't prevent re-initialization, since the values in the component do change when you change your user object.
The solution is to use a third-party state management solution that relies not on Context but on its own diffing. Redux, Zustand, and other popular libraries do their own comparison so that only affected components re-render.
Context is really only recommended for values that change infrequently and would require full-app re-renders anyway, like theme changes or language selection. Try replacing it with a "real" state management solution instead.

CSS crashes when I reload page

I am building a site with Gatsby.
I am using a component that imports a script and returns a form.
The problem is, that after you loaded the page that shows the form, and then you click to any other page and go back to that form page, the css fully crashes for the entire site and you have to refresh the whole page.
To check out what I mean click this link https://baerenherz.org/, go to the dark blue button on the very right of the menu, then click to any other navigation site and then click again on the blue button (jetzt-spenden).
Here is my component for the donation form :
import React, { useState, useEffect } from "react"
import {Helmet} from "react-helmet"
import Loading from "./Loading"
function Child() {
return(
<div style={{width: "75%", margin: "4em auto"}} >
<Helmet>
<script type='text/javascript' aysnc>
{` window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'}) `}
</script>
</Helmet>
<div className="dds-widget-container"></div>
</div>
)
}
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
return ()=>{
scriptTag.removeEventListener(); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
What I noticed is, that the second time you visit the page, the Loading.... component does not even show anymore.. the Layout is displayed but as soon as the form shows, it crashes...
Since I cannot solve this issue since literally last year I would really appreciate any help with this. Thank you in advance.
Apparently, your script is breaking React's hydration when the component should be mounted/unmounted. There's no "clean" solution if there's no React-based script available. The problem here is that your script is manipulating the DOM while React manages the virtual DOM (vDOM). Changes in the DOM outside React's scope are not listened to by React and vice versa.
That said, I'd try forcing the loading and rendering of your widget each time the page loads. Something like:
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'})
return ()=>{
scriptTag.removeEventListener('load', setLoaded(false)); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
Without a CodeSandbox it's difficult to guess how the code will behave but what it's important is to detach and clean up the listeners when the component is removed from the UI to avoid breaking React's hydration process, in the return statement. From the useEffect docs:
The clean-up function runs before the component is removed from the UI
to prevent memory leaks. Additionally, if a component renders multiple
times (as they typically do), the previous effect is cleaned up before
executing the next effect. In our example, this means a new
subscription is created on every update. To avoid firing an effect on
every update, refer to the next section.
There, besides removing the listeners from the script, you can also set the loading state to false.
I've also removed the second useEffect because the idea to avoid the CSS breaking is to force the loading of the script in each page rendering. It's not an optimal solution but it may work for you. The ideal solution would be using React-based dependencies.
Another thing to take into account is to delay the trigger of your rnw.tamaro script until the DOM tree is loaded, by moving it from the Helmet to the useEffect. This should ensure that your div and the window are available.
Turns out it was a issue on their end. Since they did an update it works.

React-redux: should the render always happen in the same tick as dispatching an action?

In my react-redux application, I have a controlled text input. Every time the component changes value, it dispatches an action and in the end, the value comes back through the redux loop and is rendered.
In the example below this works well, but in practice, I've run into an issue where the render happens asynchronously from the action dispatch and the input loses cursor position. To demonstrate the issue, I've added another input with a delay explicitly put in. Adding a space in the middle of a word causes the cursor to skip in the async input.
I have two theories about this and would like to know which one is true:
This should work, but I have a bug somewhere in my production application that causes the delay
The fact that it works in the simple example is just luck and react-redux doesn't guarantee that render would happen synchronously
Which one is right?
Working example:
http://jsbin.com/doponibisi/edit?html,js,output
const INITIAL_STATE = {
value: ""
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SETVALUE':
return Object.assign({}, state, { value: action.payload.value });
default:
return state;
}
};
const View = ({
value,
onValueChange
}) => (
<div>
Sync: <input value={value} onChange={(e) => onValueChange(e.target.value)} /><br/>
Async: <input value={value} onChange={(e) => { const v = e.target.value; setTimeout(() => onValueChange(v), 0)}} />
</div>
);
const mapStateToProps = (state) => {
return {
value: state.value
};
}
const mapDispatchToProps = (dispatch) => {
return {
onValueChange: (value) => {
dispatch({
type: 'SETVALUE',
payload: {
value
}
})
}
};
};
const { connect } = ReactRedux;
const Component = connect(
mapStateToProps,
mapDispatchToProps
)(View);
const { createStore } = Redux;
const store = createStore(reducer);
ReactDOM.render(
<Component store={store} />,
document.getElementById('root')
);
EDIT: Clarifying question
Marco and Nathan have both correctly pointed out that this is a known issue in React that won't be fixed. If there is a setTimeout or other delay between onChange and setting the value, the cursor position will be lost.
However, the fact that setState just schedules an update is not enough to cause this bug to happen. In the Github issue that Marco linked, there is a comment:
Loosely speaking, setState is not deferring rendering, it's batching
updates and executing them immediately when the current React job has
finished, there will be no rendering frame in-between. So in a sense,
the operation is synchronous with respect to the current rendering
frame. setTimeout schedules it for another rendering frame.
This can be seen in JsBin example: the "sync" version also uses setState, but everything is working.
The open question still is: is there something inside of Redux that creates a delay that lets a rendering frame in-between, or could Redux be used in a way that avoids those delays?
Workarounds for the issue at hand are not needed, I found one that works in my case but I'm interested in finding out the answer to the more general question.
EDIT: issue solved
I was happy with Clarks answer and even awarded the bounty, but it turns out it was wrong when I really tested it by removing all middlewares. I also found the github issue that is related to this.
https://github.com/reactjs/react-redux/issues/525
The answer is:
this is an issue in react-redux that will be fixed with react-redux 5.1 and react v16
What middleware are you using in your Redux application? Perhaps one of them is wrapping a promise around your action dispatches. Using Redux without middleware does not exhibit this behaviour, so I think it's probably something specific to your setup.
The issue is not related to Redux, but to React. It is a known issue and won't be fixed in the React core as it is not considered a bug but an "unsupported feature".
This answer explains the scenario perfectly.
Some attempts to address this issue have been made, but as you might see, they all involve a wrapper component around the input, so it's a very nasty solution if you ask me.
Asynchronously updating without losing the position was never supported
--- Dan Abramov (gaearon)
The solution is to track the cursor position and use a ref inside componentDidUpdate() to place the cursor correctly.
Additional info:
When you set attributes in react, internally this happens:
node.setAttribute(attributeName, '' + value);
When you set value this way, the behavior is inconsistent:
Using setAttribute() to modify certain attributes, most notably value in XUL, works inconsistently, as the attribute specifies the default value.
--- https://developer.mozilla.org/en/docs/Web/API/Element/setAttribute
Regarding your question about whether rendering occurs synchronously, react's setState() is asynchronous and used internally by react-redux:
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains
--- https://facebook.github.io/react/docs/react-component.html#setstate
There is an internal joke in the team that React should have been called "Schedule" because React does not want to be fully "reactive".
--- https://facebook.github.io/react/contributing/design-principles.html#scheduling
I think react-redux and redux are totally irrelevant to your case, this is pure React behavior. React-redux eventually calls setState on your component, there's no magic.
The problem that your async setState creates rendering frame between the react rendering and browser native event is because the batch update mechanism only happens within React synthetic events handler and lifecycle methods. Can check this post for detail.

Structuring a reducer for a simple CRUD application in redux

So I'm creating what is at it's core a very simple CRUD-style application, using React + Redux. There is a collection of (lets call them) posts, with an API, and I want to be able to list those and then when the user clicks on one, go into a detail page about that post.
So I have a posts reducer. Originally I started using the approach taken from the redux real-world example. This maintains a cache of objects via an index reducer, and when you do a "get post" it checks the cache and if it's there, it returns that, else it makes the appropriate API call. When components mount they try to get things from this cache, and if they're not there they wait (return false) until they are.
Whilst this worked OK, for various reasons I now need to make this non-caching i.e. everytime I load the /posts/:postId page I need to get the post via the API.
I realise in the non-redux world you would just do a fetch() in the componentDidMount, and then setState() on that. But I want the posts stored in a reducer as other parts of the app may call actions that modify those posts (say for example a websocket or just a complex redux-connected component).
One approach I've seen people use is an "active" item in their reducer, like this example: https://github.com/rajaraodv/react-redux-blog/blob/master/public/src/reducers/reducer_posts.js
Whilst this is OK, it necessitates that each component that loads the active post must have a componentWillUnmount action to reset the active post (see resetMe: https://github.com/rajaraodv/react-redux-blog/blob/master/public/src/containers/PostDetailsContainer.js). If it did not reset the active post, it will be left hanging around for when the next post is displayed (it will probably flash for a short time whilst the API call is made, but it's still not nice). Generally forcing every page that wants to look at a post to do a resetMe() in a componentWillUnmount fells like a bad-smell.
So does anyone have any ideas or seen a good example of this? It seems such a simple case, I'm a bit surprised I can't find any material on it.
How to do it depends on your already existing reducers, but i'll just make a new one
reducers/post.js
import { GET_ALL_POSTS } from './../actions/posts';
export default (state = {
posts: []
}, action) => {
switch (action.type) {
case GET_ALL_POSTS:
return Object.assign({}, state, { posts: action.posts });
default:
return state;
}
};
It is very easy to understand, just fire an action to get all your posts and replace your previous posts with the new ones in the reducer.
Use componentDidMount to fire the GET_ALL_POSTS action, and use a boolean flag in the state to know if the posts where loaded or not, so you don't reload them every single time, only when the component mounts.
components/posts.jsx
import React from 'react';
export default class Posts extends React.Component {
constructor(props) {
super(props);
this.state = {
firstLoad: false
};
}
componendDidMount() {
if (!this.state.firstLoad) {
this.props.onGetAll();
this.setState({
firstLoad: true
});
}
}
// See how easy it is to refresh the lists of posts
refresh() {
this.props.onGetAll();
}
render () {
...
// Render your posts here
{ this.props.posts.map( ... ) }
...
}
}
We're just missing the container to pass the posts and the events to the component
containers/posts.js
import { connect } from 'react-redux';
import { getPosts } from './../actions/posts';
import Posts from './../components/posts.jsx';
export default connect(
state => ({ posts: state.posts }),
dispatch => ({ onGetAll: () => dispatch(getPosts()) })
);
This is a very simple pattern and I've used it on many applications
If you use react-router you can take advantage of onEnter hook.

Make api calls from dumb components with Redux

I want to implement dropdown with react and redux. Dropdown will be a part of other component, so, it it really "Dumb" component. But dropdown should call to api to fetch items, apply custom filters and etc. Api calls should be authenticated, tokens stored it global state. Should I pass tokens to component props? Or there is a better way to do this?
A dumb component, by definition, should be dumb: it means that it should take everything it needs "from the top", i.e. via the props.
Only a "Smart" ("connected" to Redux) Component, up the hierarchy, would deal with fetching the data using a new (async) Action, which would then modify the state when the API call returns, which would re-render your Dumb Component with its new props.
So in Redux:
Your Dumb component takes two props: one with the values coming from your API (which actually are part of your "state"), the other one a function that is called when your dropdown selected item changes. <MyDumbComponent data={this.props.data} onChange={this.onChange.bind(this)} />
Then a "Smart" component up the hierarchy will listen to that onChange function, and dispatch an Action (FETCH_DATA)
The (async) Action will call the API, and when receiving the data, call another Action (FETCH_DATA_SUCCESS) with the data
Then Redux, with a reducer, would update its state from the Action payload
Which will re-render your Component with its new props (new data), coming from the current state.
You might want to read this: http://redux.js.org/docs/basics/UsageWithReact.html
And regarding async actions: http://redux.js.org/docs/advanced/AsyncActions.html
Dumb component doesn't mean it can do anything like fetch updates, it means it's 'dumb' to the concept of redux, and knows nothing about your store or what library you're using. The benefit is that you can change your flux implementation and you only have the small bit of work to update the smart components.
For the type of scenario you're describing, you would give your <Menu> a function via props that would run when <Menu> wants to update the data. <Menu> knows nothing about Redux - it's just executing a function - but it's still able to fetch new data. Depending on the complexities, you could pass through the raw action creator (bound to dispatchAction) and have it run that.
import * as dataActions from './actions';
// We have dataActions.fetchItems() which is the action creater to get new items.
// Attach items, and the action creator (bound to dispatch) to props
#connect((store) => {
return {items: store.data.items}
}, dataActions)
class ItemsPage extends Component {
static propTypes = {
items: PropTypes.object, // the items from the store, from #connect
fetchItems: PropTypes.func // action dispatcher, from #connect
}
render() {
return (<Menu onChange={this.props.fetchItems} /> )
}
}
// Our 'dumb' component that doesnt know anything about Redux,
// but is still able to update data (via its smart parent)
class Menu extends Component {
static propTypes = {
onChange: PropTypes.func // same as fetchItems
}
render() {
return (<button onClick={this.props.onChange}>Update items</button>);
}
}

Resources