Inside react-relay app I need to change my sidenav component based on state of main content component.
Is it any simple global store for client component communication? Would it be ok to use dummy mutation and bounce back or should I keep my state in least common parent?
If you use Redux you can connect your App with the global state. Then both your components can be set-up by passing the same prop to them.
Let say you define your App. In render you should have something like:
render() {
const {myValue} = this.props;
return (<div>
<MyFirstComponent propValue={myValue}/>
<MySecondComponent propValue={myValue}/>
</div>
);
}
Then at the end of the App
App.propTypes = {
myValue: PropTypes.object
};
function mapStateToProps(state) {
return {
myValue: state.myReducer.myValue
}
}
export default connect(mapStateToProps)(App)
Everytime you dispatch an action which will return an updated value of myValue both the components will be sharing it.
Related
I've an application when most of the data are stored in the store but the selected item is provided thought the usage of a React.Context.
React-Redux provide the connect api that accept a mapStateToProps function with state and props as a component.
What I would like, if it didn't break the hooks, is something like:
function mapStateToProps(state){
const selectedItemId = useContext(MySelectedItemContext)
return {
item: state.items[selectedItemId]
}
}
but of course it is not possible since I'm outside of the component and cannot invoke the useContext.
So, I tried to use the old API context:
function mapStateToProps(state, props){
return {
item: state.items[props.id]
}
}
export default connect(mapStateToProps)((props) =>
<MySelectedItemContext.Consumer>
{ selectedItemId => <Component id={selectedItemId} {...props}/> }
</MySelectedItemContext.Consumer>)
but this still not works because the connect returns a new component that has the consumer inside instead of outside and id prop is not defined yet in mapStateToProps.
Any idea?
The best way is to remove mapStateToProps and use useSelector hooks and Redux selectors. But if you need mapStateToProps, then you can wrap your component that must be connected to Redux into another component that will get value from context and will pass it to a component that uses Redux.
// Use this component
export function ContextConsumerComponent() {
const selectedItemId = useContext(SelectedItemIdContext);
return <ReduxConsumerComponent id={selectedItemId} />;
}
function mapStateToProps(state, props) {
return {
item: state.items[props.id]
}
}
const ReduxConsumerComponent = connect(mapStateToProps)((props) => {
// props.item will be here
});
In my React application, i am developing a screen using Functional Component.
I want to populate the data on the screen with mock data until my API is ready. I basically have an Accordion with open state where i want to display the names. Right now, i am hardcoding name to populate that. Later, i will be replacing with the actual data coming from Store. So to do that i am defining the hard coded data like below in my component itself as after the API is ready, my props will have all the required data.
function MyComponent (props) {
props={
history: {},
data:[
{
name:’XYZ’,
},
{
name:’ABC’,
}
]
}
return (
<div>Content goes here </div>
)
}
This throws me error. I want to understand if i am doing this correctly as i need to read the data values inside my div.
Props are immutable, so you should not change them.
Instead, you could mock the props that you are passing to MyComponent
Example:
In the parent component:
function MyApp() {
const mockProps={
history: {},
data:[
name:’XYZ’,
]
}
return <MyComponent {...mockProps}/>
}
and in MyComponent
function MyComponent (props) {
// do something with props here
return <div>Content goes here </div>
}
It is probably best to mock this data as coming from parent container, that way when you add Redux library later, you can simply change /where/ props are being sent from.
e.g
in Parent JS:
const Parent = props => {
const [accordionData, setData] = useState(['#1', '#2', '#3'])
/ ... rest of component /
return <Accordion accordionData={accordionData} />
}
in
const Accordion = props => {
const { accordionData } = props // pull props from parent component.
const mapAccordionData = accordionData.map(el => { return <div key={el}>el</div> })
return mapAccordionData
}
Something like this should work.
ParentJS is feeding the accordion an array of data via parent->child props. Later this will be store->child props. Accordion (child) is then rendering that data to so the user can see it via divs.
You would pass props into a component. They should come from either a parent's component state or a store (like Redux store).
function MyComponent(props) {
return <div>{props.hello}</div>;
}
class ParentComponent extends React.Component {
state = {
hello: 'Hello World!',
}
render() {
return <MyComponent hello={this.state.hello} />;
}
}
You can also pass a function that changes the state of the parent making the props also change for MyComponent. It won't mutate them but rather return a new state and therefore pass a new set of props.
Let me know if you need any further explanation.
I'm still new to react/redux, after getting something like this to function
User.js
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
return (
<Profile />
)
}
export default connect(null, {fetchUser})(User);
Profile.js
class Profile extends React.Component {
render() {
const { user } = this.props
return (
<h1>{user.profile.name}</h1>
)
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {})(Profile)
actions.js
export const fetchUser = (.....) => dispatch => {
fetch()
.....
}
reducers.js
case FETCH_USER:
return {
...state,
user: action.payload.user
};
As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.
After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md
It looks like things can be simplified a bit without using classes.
If I were to change the User and Profile components to a more functional way, how would I do it?
eg.
const User = () => {
return (
<Profile />
)
}
how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?
or am I just over complicating things?
There is also a way to support lifecycle methods in functional components.
https://www.npmjs.com/package/react-pure-lifecycle
import React from 'react';
import lifecycle from 'react-pure-lifecycle';
// create your lifecycle methods
const componentDidMount = (props) => {
console.log('I mounted! Here are my props: ', props);
};
// make them properties on a standard object
const methods = {
componentDidMount
};
const FunctionalComponent = ({children}) => {
return (
<div>
{children}
</div>
);
};
// decorate the component
export default lifecycle(methods)(FunctionalComponent);
I think you should keep using statefull components with redux...
https://medium.com/#antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.
Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}
componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.
So — something inside pure components is very soiled and absolutely impure. It is driven by side effects!
But this bypass straight logic flow, and works just differently from the rest of application.
So — just be careful. And don’t forget about magic.
Aaaand….
And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
redux-connect is a source of a change propagation.
redux connect is the end of a change propagation. It is still PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.
Stateless components are not pure, and may run slower than PureComponents by any kind.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component.
No choice. But, now — you know hidden truth, and knew some magic…
React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.
Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.
If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.
If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples
In Your case you can continue with statefull components no wrong in that
,If you need to go with functional way
There is a work arround
https://github.com/mobxjs/mobx/issues/162
Suggestion
Calling the api in componentDidMount will make sense than
componentWillMount , Because you can show the user something is
fetching.
I think,User component is designed nicely.It will act as a container for Profile to provide the Data.
Instead of making Profile component class oriented,it should be Stateless.
Lets User component pass the required data for Profile component.
You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.
Profile
const Profile = (props) => {
const {user, likeProfile} = props;
//likeProfile()//call like this using dom event or programmatically.
return (
<h1>{user.profile.name}</h1>
)
}
You need to make some changes in User component.
Get the state for Profile component via mapStateToProps.
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
const {user, likeProfile} = this.props;
return (
<Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
)
}
Map the user state for Profile in User connect.
const mapStateToProps = (state)=>{
return{
user : state.somereducerkey.user //this will be accessible in Profile via props { user}
}
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);
On my React + Redux client app, I need to get the active user info (fetch from myapp.com/users/me) and keep it somewhere so that I can access it from multiple components.
I guess window.activeUser = data would not be the best practice. But I could not find any resource about the best practice of doing that. What would be the best way to do what I want?
you can keep it in a separate reducer, and then import multiple parts of your state with connect() in your components.
Say if you have 2 reducers called users.js and tags.js which are combined with combineReducers when setting up your store. You would simply pull different parts by passing a function to your connect() call. So using es6 + decorators:
const mapStateToProps = state => {
return {users: state.users, tags: state.tags}
}
#connect(mapStateToProps)
export default class MyComponent extends React.Component {
and then down in your render function:
return (
<div>
<p>{this.props.users.activeUsernameOrWhatever}</p>
<p>{this.props.tags.activeTags.join('|')}</p>
</div>
);
So your different reducer states become objects on this.props.
You can use React's context, HOC or global variables to make your data available to multiple components, via context
Something like...
class ParentDataComponent extends React.Component {
// make data accessible for children
getChildContext() {
return {
data: "your-fetchet-data"
};
}
render() {
return < Child />
}
}
class Child extends React.Component {
render() {
// access data from Parent's context
return (<div> {this.context.data} </div>);
}
}
create an action creator and call it on componentWillMount of the appropriate component so it runs right before your component mounts, and in the action creator fetch the data you need and pass it to a reducer. in that reducer you can keep the data you want throughout your application. so whenever you needed the data you can retrieve it from redux state. this tutorial from official redux website covers everything you need to know. mention me if you had any questions.
I'm wondering how other people are handling passing redux action creators from a smart top-level component down to many lower level dumb components without bloating their props definitions.
For example, following this excellent tutorial on redux, if I pass a list of action creators into the props like so
import Voting from './Voting';
import * as actionCreators from '../action_creators';
...
export const VotingContainer = connect(
mapStateToProps,
actionCreators
)(Voting);
then in my Voting component I have access to the actionCreators which is really cool.
But if I have say 20 actionCreators that are used in Voting and all of its child components, eg.
Voting -> VotingContainer -> VotingDetail -> VotingFoo -> VotingBar
then I end up with render functions that look like this
class Voting extends React.Component {
render(){
<VotingContainer
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator15={this.props.actionCreator15} />
}
}
class VotingContainer extends React.Component {
render(){
<VotingDetail
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator12={this.props.actionCreator12} />
}
}
.
.
.
class VotingFoo extends React.Component {
render(){
<VotingBar
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator6={this.props.actionCreator6} />
}
}
Is there a best practice for this situation, a way to group the actionCreators together somehow without a lot of boilerplate at each step ? I haven't seen anything in any of the tutorials/examples...
Just connect components below the tree to Redux too.
We over-emphasize “one container at the top” in the examples.
This makes sense when we’re talking about very simple apps.
For any complex app, as soon as passing props gets tedious, connect() components below.
I cover this in my free videos: see Extracting Container Components and the next several videos.
I find that in most cases where I have a lot of dumb wrappers around a core ui component, most of the props from the top container are needed in the most nested component.
Because of this, ES6 ... syntax helps a lot.
You can do this:
<VotingBar {...this.props} />
Which is equivalent to this:
<VotingBar
actionCreator1={this.props.actionCreator1}
.
.
.
actionCreator6={this.props.actionCreator6} />
To avoid passing propertied from level to level down to where those props are actually used, React Context could be used to wrap the top Child with context (some specific data).
Codepen Demo
Here's a simple use-case example, where more than one reducer is defined, each of the reducers is responsible for its own state (a counter in this example)
A <Button> is inside a <Modal> and two Modal components are inside App, each one should eventually "broadcast" the change made internally (in the deepest component) to the top component (App) which is actually listening to the changes and acting on them.
Only App cares about changes in Button but since there can be many Button components, the App must know which deep component did what action and dispatch the right data back to Redux.
The Modal is simply something in between which exists for representational purposes only. it doesn't care about any props or state. Button also doesn't care about anything except what is being sent to it directly via the Context. he listen to changes via the Consumer method from React.createContext
const { Provider:ContextProvider, Consumer:ContextConsumer } = React.createContext({});
const {connect, Provider } = ReactRedux;
const {createStore, compose, combineReducers} = Redux;
const {Component} = React;
//// REDUX STORE STUFF /////////////////////////////////////
function reducer_foo(state = {value:1}, action){
if( action.type == 'FOO__SET_VALUE') // type should be unique
return { ...state, value:action.value }
else return state
}
function reducer_bar(state = {value:1}, action){
if( action.type == 'BAR__SET_VALUE') // type should be unique
return { ...state, value:action.value }
else return state
}
const rootReducer = combineReducers({
foo: reducer_foo,
bar: reducer_bar
});
//// REACT STUFF /////////////////////////////////////
// 2nd depth-level
// This component's "job" is to simply take a value and return a manipulated value to
// whoever called it. This is a very simplifed component, but think of a datepicker instead.
const Button = () =>
<ContextConsumer>
{v => <button onClick={()=> v.action(v.value + 1, v.name)}>Click to INC: {v.value}</button>}
</ContextConsumer>
// 1st depth-level (in reality this level will be more complex)
const Modal = () => <p><Button /></p>
// top depth-level component
class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
// The deepest component will pass the value and the name for which to dispatch to
updateValue = ( value, name ) => {
this.props.dispatch({type:`${name.toUpperCase()}__SET_VALUE`, value})
}
render(){
const {foo, bar} = this.props;
return (
<React.Fragment>
<ContextProvider value={{value:foo.value, action:this.updateValue, name:'foo'}}>
<Modal />
</ContextProvider>
<ContextProvider value={{value:bar.value, action:this.updateValue, name:'bar'}}>
<Modal />
</ContextProvider>
</React.Fragment>
)
}
}
function mapStateToProps(state){
return state // in this example let's just pass the whole state for simplicity
}
const ConnectedApp = connect(mapStateToProps)(App)
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<ConnectedApp />
</Provider>,
document.getElementById('root')
)
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js"></script>
<div id="root"></div>
There are of course a variety of ways you can tackle this issue.
Recently, I've started skipping the whole passing of action creator functions down the chain in favor of just requiring the store and my action creators directly wherever they're needed and dispatching from there, e.g.
var store = require('../store');
var actions = require('../actions');
// Somewhere inside your component...
store.dispatch(actions.someAction(data));
Just make sure the result of your action creators (i.e. the new state) is passed down through your top level components. This keeps your data flow unidirectional and easy to understand.