How to use GraphQL Mutations inside a Redux Reducer - reactjs

I have here a react stateless function that I would love to call my mutations from my reducer. Note, calling the mutation inside a React.Component Class works by means of adding a function inside the component, I dont want that, I want to use my reducer to do that.
With minimum code example, I'll show my setup
// index.js render function
[...] // imported variables
<ApolloProvider client={client}>
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<App />
</div>
</ConnectedRouter>
</Provider>
</ApolloProvider>
// Mutation
const Foo = gql`
mutation Foo($id: String) {
AddBar(id: $id) {
id
}
}
`;
// Component
[...] // other imports for below variables
const Comp = (props) => (
<div>
<button onClick={() => props.addFoo(props)}>Click</button>
</div>
)
const mapDispatchToProps = dispatch => bindActionCreators({
addFoo
}, dispatch)
export default connect(
mapDispatchToProps
)(graphql(Foo)(Comp))
// Reducer (very slimmed with just the action)
export const addFoo = (props) => {
// props contained the mutate function
props.mutate({
variables: {id: "1" }
})
// dispatch goes here but not needed.
}
Ok, I have slimmed down this example as much as possible. My issue is that my variables are not passing to my mutation function. If I hard-code id with one and click the button, graphql changes my data (yes) but the only issue is variables are not passing. In the inspector I do see the variables with correct values but...not passing to the mutate function.

A few thoughts...
First, reducers should never mutate data or make asynchronous calls. They are meant to be pure functions without side effects.
Taking GraphQL out of the picture for a moment and assuming that you have just a REST call, you'd usually mutate inside of an Action Creator or something similar. Action Creators in Redux are inherently synchronous, so you'd employ either Redux Thunks or Redux Sagas (or something similar) to help with that.
Ok, let's put GraphQL back in. As you pointed out, if you include your mutation in your component, it is difficult to wire that in to your Redux implementation. The two are kind of mutually exclusive. In your Redux implementation, however you'd normally make async fetch calls, you can use the Apollo Client without React to mutate...
const apolloClient = createApolloClient();
apolloClient.mutate({mutation: Foo}).then(handleResult)
Now, what does createApolloClient() do? You don't want to create a new one every time. That client maintains a cache and can handle all of the value-add which comes from re-using the client. That goes for the React code. You'd like for any queries to be executed from the React bindings to use the same client which you use in your Redux action creators. That createApolloClient() function needs to create a client singleton and return it so that you'd use it in your ApolloProvider as well:
<ApolloProvider client={createApolloClient()}>
I hope this helps!

Related

How to pass props from parent to grandchild component in react

I have tried pass value from parent to grandchild component, and it works. While I am thinking if there is another simpler or other way of passing props in shorter path.
What I did is quite cumbersome in codesandbox
There may be a common problem in react world called prop drilling by passing data to children only using props.
I would recommend only 2-level passing, if you need pass data deeper then you probably doing something wrong.
Use one of popular state management library (if your project is big) or React context (which is awesome)
Create a folder called /contexts and put contexts there. The structure of files can be like shown below:
First you need to create a context itself
type ClientContextState = {
data: User;
set: (data: User) => void;
logout: () => void;
};
// empty object as a default value
export const UserContext = createContext<UserContextState>({} as UserContextState);
Then create a Provider wrapper component
export const UserProvider = ({ children }: Props) => {
const [data, setData] = useState<User>({});
const sharedState = {
data,
set: setData
logout: () => setData(null)
}
return <UserContext.Provider value={sharedState}>{children}</UserContext.Provider>
});
You may also want to have an alias of useContext hook:
export const useUser = () => {
return useContext(UserContext);
};
After all this whenever you wrap your components or app to <UserProvider>...</UserProvider> you can use our hook to access data and methods form sharedState from any place you want:
export LogoutButton = () => {
const {data, logout} = useUser();
return <Button onClick={() => logout()}>Logout from {data.name}</Button>
}
Whenever you want to pass props or data from Grandparent to child component, always use react-redux. This is useful to maintain the state and access the data from anywhere/any component.
Another way is to use useContext hooks which you can use to pass the props
Following are the steps to use useContext hooks
Creating the context
The built-in factory function createContext(default) creates a context instance:
import { createContext } from 'react';
const Context = createContext('Default Value');
The factory function accepts one optional argument: the default value.
Providing the context
Context.Provider component available on the context instance is used to provide the context to its child components, no matter how deep they are.
To set the value of context use the value prop available on the
<Context.Provider value={value} />:
function Main() {
const value = 'My Context Value';
return (
<Context.Provider value={value}>
<MyComponent />
</Context.Provider>
);
}
Again, what’s important here is that all the components that’d like later to consume the context have to be wrapped inside the provider component.
If you want to change the context value, simply update the value prop.
Consuming the context: Consuming the context can be performed in 2 ways.
The first way, the one I recommend, is to use the useContext(Context) React hook:
import { useContext } from 'react';
function MyComponent() {
const value = useContext(Context);
return <span>{value}</span>;
}
Generally it's helpful to consider whether moving state down the hierarchy would be the simplest route. That means lifting the component instantiation to a place closer to the state being used. In your example, that could mean Component_data is used inside Component and passed to its children there, removing one step in the nested data flow. Even better, would be that Child.a accesses Component_data.A directly.
In a real app with cases where accessing the data directly is less feasible, a solution I lean towards is using Context to set data in the parent that retrieves it, and then I can access it however deeply nested the component might be that needs it.
i.e. in App I would create the Context provider, and in ChildA I access it via useContext hook.
Further reading
https://reactjs.org/docs/context.html
https://overreacted.io/before-you-memo/#solution-1-move-state-down (this post is about an alternative to using useMemo but has an illustrative example of why moving state down is a good thing)

Access React context value from redux-saga function

I have some variables which are controlled by React Context through React.createContext() with Provider and Consumer component. I'm also using redux-saga, so my question is are there anyway to access this context value in redux-saga function. I'm looking for something like "yield select" statement that works with redux-store.
Anyone who know it please explain to me. Thank you in advances.
P/S: Currently, I pass context value to action object payload when dispatch an action. So in redux-saga, we can use it in action parameter. I don't know whether it's the best practice.
EDIT: There is now a package that allows you to grab context easily, react-outside-call
You can keep an outside reference of your provider component, and grab the state from that.
Something like this...
Provider component
class AppProvider extends Component {
state = {
number: 10,
}
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
)
}
}
Apps main entry file, or where ever you reference that provider instance.
let appProviderRef
export const getProviderState = () => appProviderRef.state
ReactDOM.render(
<AppProvider ref={ref => appProviderRef = ref}>
<App />
</AppProvider>
, document.getElementById('root'))
You can now grab the state from within your redux sagas (or anywhere you want) synchronously calling getProviderState

What is going on in this Redux code...confused about mapDispatchToProps, dispatch, and connect

I'm following a react tutorial and I had a few questions.
I am confused about this function:
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchWeather }, dispatch)
}
I want to breakdown each part of this function.
How do you hook up the action creator to a container? How is this.props.fetchWeather working code? Is this responsible for that?:
function mapDispatchToProps(dispatch) {
If so, what is this doing?
return bindActionCreators({ fetchWeather }, dispatch)
Is that responsible for making sure that the action object that is returned from the action creator flows down into the middleware and reducers?
What is this doing then:
function mapDispatchToProps(dispatch)
Is mapDispatchToProps just convention or is it part of react?
Lastly, what is this export doing:
export default connect(null, mapDispatchToProps)(SearchBar);
What is the connect doing? What does it do for me?
Here is my whole code for reference:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchWeather } from '../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({ term: event.target.value });
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchWeather(this.state.term);
this.setState({ term: '' });
}
render() {
return (
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Get a five-day forecast in your favorite cities"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className='input-group-btn'>
<button type="submit" className="btn btn-secondary">
Submit
</button>
</span>
</form>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchWeather }, dispatch)
}
export default connect(null, mapDispatchToProps)(SearchBar);
A very concise answer would be:
mapStateToProps() is a utility which helps your component gets updated state(which is updated by some other components)
mapDispatchToProps() is a utility which will help your component to fire an action event (dispatching action which may cause a change of application state)
bindActionCreators() is mostly used when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.
<Provider> makes the Redux store available to the your "connected" React components.
connect() is a higher-order component (HOC) that lets you inject Redux state into a regular React component.
Let's dive a bit deeper
ReactJS - JS library for building user interfaces.
Redux - JS library for managing application state.
Putting them together using react-redux:
If we want to link our React application with the Redux store, we first have to let our app know that this store exists. This is where we come to the first major part of the react-redux library, which is the Provider.
A Provider is a React component given to us by the react-redux library. It serves just one purpose: to “provide” the store to its child components.
Provider
Makes the Redux store available to the connect() calls in the component hierarchy below. Normally, you can’t use connect() without wrapping a parent or ancestor component in <Provider>. So ultimately connect does just that, it connects your React app to the Redux store.
//This is the store we create with redux's createStore method
const store = createStore(todoApp, {})
// Provider is given the store as a prop
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app-node')
)
Props
store (Redux Store): The single Redux store in your application.
children (React Element): The root of your component hierarchy.
Explanation and usage of the Connect function:
Now that we have “provided” the redux store to our application, we can now connect our components to it. We established previously that there is no way to directly interact with the store. We can either retrieve data by obtaining its current state or change its state by dispatching an action (we only have access to the top and bottom component of the redux flow diagram shown previously). This is precisely what connect() does.
To use connect(), you need to define a special function called mapStateToProps that describes how to transform the current Redux store state into the props you want to pass to a presentational component you are wrapping.
In addition to reading the state, container components can dispatch actions. In a similar fashion, you can define a function called mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
Simple explanation and a basic example of mapStateToProps
The Store is a box, which contains the entire application state. Imagine that this box is in an unknown location.
The components need to take some of the things out of the box but they only need some of the things stored in it. The components know what they need from the box but they don't know where the box is.
The mapStateToProps function is a filter used to select which things in the box are required by the component. The selected things become the component properties.
The mapStateToProps function is not enough because it selects the required things in the box but it doesn't know where the box is located.
The connect function knows where the box is located and passes it to the mapStateToProps function so it can grab what it needs.
Conclusion: mapStateToProps simply returns a specified part of the current state. mapStateToProps get the data that is fed to its component.
const mapStateToProps = (state) => {
return { things: state.things }
};
So now we're able to use that part of the state as props -> this.props.things
But what if the component wants to change the state? That is where mapDispatchToProps comes in.
Simple explanation and a basic example of mapDispatchToProps
As implied in its name, this function directs the dispatching or sending of an action by pointing it to an action creator. For example:
const mapDispatchToProps = () => {
return {
addThing: addThing,
doAnotherThing: doAnotherThing
}
}
mapDispatchToProps takes the dispatch functions in your component and executes them against the Redux reducer when that function is fired. Remember that Props aren’t just objects, they can also be functions. This is where mapDispatchtoProps applies. MapDispatchToProps allows for you to dispatch state changes to your store. An example would be a button click that triggers a refresh or an automatic loading of data once the component is mounted.
The action creator is made available to the component as a prop, which is usually tied to an event handler function contained in the component:
handleOnClick() {
this.props.addThing();
};
However, returning the action creator is only one part. We also want the send that returned action to the store. How do we do that? We use Redux’s bindActionCreators().
Simple explanation and a basic implementation of bindActionCreators():
Turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.
Normally you should just call dispatch directly on your Store instance. If you use Redux with React, react-redux will provide you with the dispatch function so you can call it directly, too.
The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.
To implement it, we:
import { bindActionCreators } from 'redux';
...
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
addThing: addThing,
doAnotherThing: doAnotherThing
}, dispatch);
};
The bindActionCreators() function accepts the action creator and the store’s dispatch function as arguments, and returns a dispatch function that uses the return value of the action creator as its arguments.
Once again, tying this all together is the connect() function, in which we pass mapDispatchToProps as a second argument. For example:
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
which will export a component that can both get the current state from the store, and dispatch an action to the store to trigger and update to the state.
Now, let's put it all together with a little TodoItem.js React functional component:
import { connect } from 'react-redux'
const TodoItem = ({ todo, destroyTodo }) => {
return (
<div>
{todo.text}
<span onClick={destroyTodo}> x </span>
</div>
)
}
const mapStateToProps = state => {
return {
todo: state.todos[0]
}
}
const mapDispatchToProps = dispatch => {
return {
destroyTodo: () =>
dispatch({
type: 'DESTROY_TODO'
})
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoItem)
mapStateToProps and mapDispatchToProps are both pure functions that are provided the stores “state” and “dispatch” respectively. Furthermore, both functions have to return an object, whose keys will then be passed on as the props of the component they are connected to.
In this case, mapStateToProps returns an object with only one key: “todo”, and mapDispatchToProps returns an object with the destroyTodo key.
The exported connected component provides todo and destroyTodo as props to TodoItem functional component

How to make react app listen to redux store conditionally?

I have a React app for University Management.
Below is my router:
<Route path="selecting/" component={SelectUniversity}>
<Route path="myUniversity" component={MyUniversity} />
<Route path="studentdetails" component={AddStudentDetails} />
<Route path="payment" component={Payment} />
</Route>
the flow is==>MyUniversity==>AddStudentDetails==>Payment
As per this, everything is working as expected
All the three components MyUniversity, AddStudentDetails, Payment are extensivly using redux store
MyUniversity's mapStateToProps is as follows
function mapStateToProps(state) {
const { results } = state.results
const studentData = state.results.studentData
return {
results,
studentData,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ ...Actions, ...studentActions }, dispatch)
}
there are many store variables involved, this is for sample purpose
Similarly, separate mapstaetoprops and mapdispatchtoprops for other two component.
Now the requirement is (for some unavoidable reasons):-
if a user directly lands on myuniversity page with a id like this below:-
http://mywebsite.com/myuniversity/9999,
I need to get the data associated with 9999 (which am already getting) and execute the same flow.
my updated router
<Route path="selecting/" component={SelectUniversity}>
<Route path="myUniversity" component={MyUniversity} />
<Route path="myUniversity/:unidetails" component={MyUniversity} />
<Route path="studentdetails" component={AddStudentDetails} />
<Route path="payment" component={Payment} />
</Route>
Once I get the data how can I update the redux store so that the existing flow will work as expected.
I know I can dispatch as many actions as i want once we get the data from ajax call but like I said there are 15-20 different state variables are involved for each of the three component. So, it does not seem like a scalable approach to fire so many dispatchers on each component load.
Approach 2
So I came up with another approach:-
1. Define a new reducer.
2. Upon getting data store the entire ajax result in your desired format in the state.
3. Now go to the mapstatetoprops of each of the three components and add conditions on every every prop level whether get data from pevious reducer or current reducer.
for example:-
lets say i have added another reducer called universitydetails
then my mapstatetoprops will look something like this
const { results } = state.results || state.universitydetails
const studentData = state.results.studentData || state.universitydetails.studentData
return {
results,
studentData,
}
then again, adding condition at each prop level (given that i am using 15-20 different state variables in each of the three components)
Approach 3:-
add redux-saga and dispatch action on myuniversity load and yield other actions based on it
But this wont be generic. Incase, I want to add simliar feature for other things such as hostel then again i need to add sagas for this hostlels and need to dispatch action on initial load.
What I think will be best approach (correct me if am wrong) :-
Define a new reducer and somehow make my component listens to this reducer(like approach 2 but without uglifying the code) so that in case i want to add another feature like hostelselector, i just need to update my new reducer structure and make my component listen to this new reducer
I am stuck at somehow
Any suggestion how to go about this?
OK I think I understood how's the flow of your application and that's my idea.
You can create one reducer that will respond to just one action called 'SET_SOURCE'. There it will put the name of the current source where you should extract your data from. Ideally it should be the name of the reducer/object where your data will be hold.
After that you have to create a reducer for each source. Every source will be responsible of itself and they won't interact each other. That means that when your ajax call will be finished and you will have to save your data inside inside the store (aka firing an action), you will fire the action to trigger the reducer that you want.
The structure of your reducers could be like this:
=> myUniversity
=> currentSource
=> sources
=> uniDetails
=> hostel etc.
You can achieve this kind of structure using combineReducers function
// myUniversityReducer.js
import uniDetails from 'uniDetailsReducer'
import hostel from 'hostelReducer'
import currentSource from 'currentSourceReducer'
const sources = combineReducers({hostel, uniDetails})
const myUniversity = combineReducers({currentSource, sources})
export myUniversity
Inside your mapStateToProps you could something like to select the current source:
function mapDispatchToProps = (state) => {
const currentSource = selectCurrentSource(state) // currentSource = 'uniDetails' => name of the reducer
const dataFromSource = selectDataFromSources(state, currentSource) // dataFromSource = state[currentSource] => object inside uniDetails
// ... if here you need to manipulate data because the structure of every
// source is different, you can have your function that will do that based on the source name
return { ...dataFromSource }
}
That's my idea but there might be the chance that I missed something or I misunderstood some of the scenario

How to pass JSON to Redux -> React Component

I am building an app from this boilerplate: https://github.com/werein/react
I am hitting an API that requires a Secret Key, so I am going to Fetch the JSON via Express (server.js) and pass it to my component as props.
How do I glue everything together to get the JSON in as props?
I tried just to pass some Dummy JSON
app.get('yourinfo', (req, res) => {
res.json({ 'a': 'Some JSON' });
});
<ConnectedRouter history={history}>
<Layout>
<Match exactly pattern="/yourinfo" component={App} />
</Layout>
</ConnectedRouter>
And I don't get anything rendered in when I inspect this.props.
Any ideas?
Remember when using redux that your application's state is stored in the redux state. And all changes to the state are done through actions which are dispatched.
So, to save your API key in your application's state, just create an action & dispatch it:
// the "save API key" action
export function saveAPIKey(key) {
return {
type: APP_APIKEY,
key,
};
}
// to use the loadAPIKey action, dispatch it
import store from './store/store';
import { saveAPIKey } from './path/to/actions';
fetch('/my/api/endpoint')
.then((response) => response.json())
.then((data) => store.dispatch(saveAPIKey(data.key)));
That's the basic idea, but there is a simpler way of doing it. If you read through the redux docs on async actions you'll see that you can use the redux-thunk package to create an action that behaves asynchronously. It looks a little more complicated, but it has the advantage of putting all the code handling the asynchronous actions in one place, rather than spreading fetch calls throughout your code.

Resources