The following React component is given:
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { store, StoreState } from "../../redux/actions";
import { setBackgroundAction } from "../../redux/title.actions";
import "./Loader.scss";
interface ReduxProps {
bgClass: string;
}
interface Props extends ReduxProps {
bgChange?: boolean;
}
export default function Loader(props: Props) {
const [bgClassOld, setBgClassOld] = useState<string>("");
const dispatch = useDispatch();
useEffect(() => {
const { bgChange, bgClass } = props;
if (bgChange) {
setBgClassOld(bgClass);
dispatch(setBackgroundAction("bg-white"));
dispatch(setBackgroundAction(bgClassOld));
}
});
return (
<div className="d-flex">
<div className="loader">
<img src="/loadscreen.gif" />
</div>
</div>
);
}
// function mapping(state: StoreState): ReduxProps {
// return {
// bgClass: state.title.backgroundClass,
// };
// }
This is more a theoretical question to see how to actually do the following change:
The component Loader will be imported from another npm package (shared components).
My problem is that I have a redux state in the current implementation included (changed it from Class to Functional component, so thats mapping() is still in there).
As I only import the component in my "main" client, I will not have the whole redux setup in place. So I think I need to pass the store and the dispatch functions via props.
So should I create a prop store for my component, where I pass the redux store when I import the shared component?
Do I also create two props for each dispatch functions?
Does is make sense or would there be a better approach?
You generally shouldn't import the Redux store directly into components. The hooks allow your component to access whatever Redux store has been injected into the component tree by a <Provider>.
You also don't need to pass dispatch as a prop. Any component can call useDispatch(), and dispatch actions to whatever Redux store is actually being used.
If I understand your question, you're planning on importing this component into an existing app, and it sounds like that app is already configured to use (React-)Redux with a <Provider> at the top. If that's the case, then you don't have to do anything else special to make this work. Just call the React-Redux hooks in any of your components.
Related
I've a class based component in which I'm calling the redux store through a action.
class App extends Component {
//Calling Constructor & setting State & other things
this.executeAction("someAction");
}
I want to know what would be the alternate for this in functional Component.
P.s:- I'm not sure if it is some library.
You can use useDipatch hook from react-redux
import { useDispatch } from 'react-redux'
function App() {
//Calling Constructor & setting State & other things
const dispatch = useDispatch()
dispatch("someAction")
}
My question is related to react-admin repo.
I want to dispatch an action, outside of scope of a component, in order to do that, I've read that I need to get access to the actual redux store itself, and dispatch on in directly,
so I know that the Admin component has an initialState prop, but it only accepts default state object, not the store. So I can't make a store and pass it in.
My question is:
How do I access redux store of an Admin component?
How can I dispatch an action outside of a component, when using Admin as my main app component?
my current app entry looks like this:
<AppLayoutDirection>
<Admin
title="My App"
locale="en"
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
theme={themeProvider}
customSagas={customSagas}
appLayout={AppLayout}
>
{DynamicResource}
</Admin>
</AppLayoutDirection>
When you say that you need to dispatch an action outside the scope of a component, I suppose that it's in reaction to another action that was dispatched in the past.
In that case, that's what react-admin calls a side effect. React-admin handles side effects using redux-saga. Here is how to create a custom saga:
// in src/bitcoinSaga.js
import { put, takeEvery } from 'redux-saga/effects';
import { showNotification } from 'react-admin';
export default function* bitcoinSaga() {
yield takeEvery('BITCOIN_RATE_RECEIVED', function* () {
yield put(showNotification('Bitcoin rate updated'));
})
}
Register this saga in the <Admin> component as follows:
// in src/App.js
import React from 'react';
import { Admin } from 'react-admin';
import bitcoinSaga from './bitcoinSaga';
const App = () => (
<Admin customSagas={[ bitcoinSaga ]} dataProvider={simpleRestProvider('http://path.to.my.api')}>
...
</Admin>
);
export default App;
This is documented in the react-admin documentation, in the <Admin> chapter.
You could also simply use custom reducers if the computation is no async
// in src/App.js
import React from 'react';
import { Admin } from 'react-admin';
import reducers from './reducers';
const App = () => (
<Admin customReducers={customReducers} dataProvider={simpleRestProvider('http://path.to.my.api')}>
...
</Admin>
);
export default App;
I have started learning basic of Context API in ReactJS.
This is a part of reactJS code which uses reactJS.
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import LeftPane from './LeftPane';
import RightPane from './RightPane';
import {SampleProvider} from './sample';
const App =()=>{
return(
<SampleProvider>
<div className="panes">
<LeftPane/>
<RightPane/>
</div>
</SampleProvider>
)
}
export default App;
ReactDOM.render(
<App/>,
document.getElementById('root')
)
RightPane.js
RightPane.js
import React from 'react';
import Receives from './Receives';
const RightPane =()=>{
return(
<div className="pane">
<Receives/>
</div>
)
};
export default RightPane;
sample.js
import React,{Component , createContext} from 'react';
const Context = createContext();
const {Provider, Consumer : SampleConsumer}=Context;
class SampleProvider extends Component{
state={
value:'default value'
}
actions={
setValue:(value)=>{
this.setState({value});
}
}
render(){
const {state,actions}=this;
const value={state,actions};
return(
<Provider value={value}>
{this.props.children}
</Provider>
)
}
}
export{
SampleProvider,
SampleConsumer
};
Receives.js
import React from 'react';
import {SampleConsumer} from './sample';
const Receives = ()=>{
return(
<SampleConsumer>
{
(sample)=>(
<div>
Value:{sample.state.value}
</div>
)
}
</SampleConsumer>
)
}
console.log(Receives);
export default Receives;
Everything is fine. I understand everything except the function in SampleConsumer
component.
function in SampleConsumer uses sample as parameter.
I tested and sample.state.value renders 'default value' and it is the value of the state which is declared in SampleProvider component.
SampleProvider passes down the state as props to Provider component. I understand
Provider can use that state. But how the parameter in SampleConsumer understands
state in SampleProvider component? I have never passed the state as props to
SampleProvider component ..(I understood so. Maybe it's wrong)
I read this documentation
https://reactjs.org/docs/context.html
but didn't understand 100%
Everything is fine. I understand everything except the function in SampleConsumer component.
You have set SampleConsumer to point to the raw Consumer output of createContext(). It will function exactly the same as the ThemeContext.Consumer example in the docs.
function in SampleConsumer uses sample as parameter. I tested and sample.state.value renders 'default value' and it is the value of the state which is declared in SampleProvider component.
You have wrapped the raw Provider output of createContext() with your SampleProvider component. As you did so, you set the Provider's context value to (initially) be:
{
state: {
value: 'default value'
},
actions: {
setValue: (value) => { this.setState({value}) }
}
}
Meaning that whenever you invoke SampleConsumer that is a child of SampleProvider, the argument in the "child as a function" will be passed that value. In other words, this would display the string representation of the object in the above snippet:
<SampleConsumer>
{ (value) => <div>{value.toString()}</div> }
</SampleConsumer>
SampleProvider passes down the state as props to Provider component. I understand Provider can use that state.
Correct - you have set Provider's value prop to be equal to an object that contains SampleProvider's state.
But how the parameter in SampleConsumer understands state in SampleProvider component?
This is exactly what the context API accomplishes. SampleConsumer has access to Provider's value prop, without needing to pass the prop through all the child elements in between. Note that your code here doesn't have anything in between, so it's a little trivial; the docs you linked provide a better example.
I have never passed the state as props to SampleProvider component ..(I understood so. Maybe it's wrong)
You passed SampleProvider's state as a prop to Provider. Provider, in turn, passed its prop down to SampleConsumer.
I think the core of the misunderstanding here is your use (or naming) of SampleProvider. I'm not sure what you're trying to do with that state, but it's not really a "Provider" anymore and makes things confusing. This is unlike your SampleConsumer, which is still the default Consumer, just renamed.
I have some experience with ReactJS but now I am trying to start using Redux and I have encoutered several problems. I already know how to create actions, consts, reducers, how to connect them to one single store, but I don't actually now how to use it with React. For example I have a form to gather user's data and I want it all passed to Redux store. So I guess the main question would be how do I trigger the action in ReactJS?
when using react-redux, you'll get a component enhancer called connect.
class Component extends React.Component {
render() {
return (
<button onClick={this.props.onClickButton}>
{this.props.a}
</button>
)
}
}
export default connect(function mapStateToProps(state) {
return { a: state.store.a }
}, { onClickButton: incrementAction })(Component)
What I'm doing here is taking a global store value (state.store.a - state is the global store, .store is the store from a combined store, and a is the value), and telling the React component to listen for changes on this variable (transparently through connect).
Additionally, I'm wrapping an action creator incrementAction (and renaming it to onClickButton). If you're using a middleware like redux-thunk, this will automatically pass in store.dispatch as an arg. Otherwise, this is a standard action creator.
both of these will be available inside the component as props (the args are descriptively named mapStateToProps and mapDispatchToProps)
You'll want to use react-redux. For example, here's a small counter:
import { connect } from "react-redux";
import { increment } from "actions";
import PropTypes from "prop-types";
import React from "react";
function counter ({ count, increment }) {
return <button onClick={increment}>
{count}
</button>;
}
counter.propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired
};
export default connect(
(state) => ({
count: state.data.count
}),
{ increment }
)(counter);
The (state) => ({ }) bit passes a property called count to the component's props. The { increment } passes your increment function in the props.
Be sure to include the { increment } part in the connect; if you don't, your redux action won't be dispatched.
To bind redux to react there is a package called react-redux. The description of which is official react bindings for redux.
You can connect the actions to react by using mapDispatchToProps, which will map your actions as props. Then you can call those actions as props. When you call those actions as props, the actions will be triggered and redux state will change.
To access the state you have to use mapStateToProps, which will give you the state as props.
You can use connect method to connect mapStateToProps and mapDispatchToProps to react.
I think it would be easier if you do a tutorial. This is a tutorial by Dan Abramov, creator of Redux.
I'm studying react and I have an example like this
//index.js
const store = createStore(reducer)
render(
<Provider store={store}>
<AddTodo />
</Provider>,
document.getElementById('root')
)
//Apptodo.js
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
.......
Why didn't it get this.pros.store but simply call the dispatch() function ?
EDIT: How does it extract the dispatch from this.pros. Isn't the object this.pros.store ? and in this case why don't we just extract store ?
Thank you.
react-redux is the library that is passing these methods to your component as props.
dispatch() is the method used to dispatch actions and trigger state changes to the store. react-redux is simply trying to give you convenient access to it.
Note, however, that dispatch is not available on props if you do pass in actions to your connect function. In other words, in the code below, since I'm passing someAction to connect, dispatch() is no longer available on props.
The benefit to this approach, however, is that you now have the "connected" action available on your props that will automatically be dispatched for you when you invoke it.
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { someAction } from '../myActions';
const MyComponent = (props) => {
// someAction is automatically dispatched for you
// there is no need to call dispatch(props.someAction());
props.someAction();
};
export default connect(null, { someAction })(MyComponent);
Or if we were to use object destructuring as shown in the example you give...
const MyComponent = ({ someAction }) => {
someAction();
};
It's important to note, however, that you must invoke the connected action available on props. If you tried to invoke someAction(), you'd be invoking the raw, imported action — not the connected action available on props. The example given below will NOT update the store.
const MyComponent = (props) => {
// we never destructured someAction off of props
// and we're not invoking props.someAction
// that means we're invoking the raw action that was originally imported
// this raw action is not connected, and won't be automatically dispatched
someAction();
};
This is a common bug that people run into all the time while using react-redux. Following eslint's no-shadow rule can help you avoid this pitfall.
Your addTodo component has access to the store's state and methods(e.g, dispatch, getState, etc). So, when you hooked up your React view with the Redux store via the connect method, you had access to store's state and methods.
({ dispatch }) is simply using JS destructuring assignment to extract dispatch from this.props object.