RTK query with Storybookjs - redux-toolkit

I am using RTK query with typescript in my react application and its working fine however storybookjs is not able to mock data for RTK query.
I am trying to mock store object as shown in this storybook document.
example -
export const Test = Template.bind({});
Test.decorators = [
(story) => <Mockstore data={myData}>{story()}</Mockstore>,
];
.
.
.
const customBaseQuery = (
args,
{ signal, dispatch, getState },
extraOptions
) => {
return { data: [] }; // <--- NOT SURE ABOUT THIS
};
const Mockstore = ({ myData, children }) => (
<Provider
store={configureStore({
reducer: {
[myApi.reducerPath]: createApi({
reducerPath: 'myApi',
baseQuery: customBaseQuery,
endpoints: (builder) => ({
getMyData: myData, //<-- my mock data
}),
}).reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(myApi.middleware),
})}
>
{children}
</Provider>
);
Since RTK query hook is autogenerated, I am not sure how to mock it in storybookjs. Instead of getting mock data storybook if trying to fetch actual data.
Please help me.

You'd do
endpoints: (builder) => ({
getMyData: builder.query({
queryFn: () => { data: myData }, //<-- my mock data
})
}),
alternatively you could leave the store setup just as it is and use msw to mock the real api.

Related

ag-grid React run on every render

I want to re-run
headerComponentFramework: (params) => {
console.log('xxx');
},
every single time the data value changes
<AgGridReact
defaultColDef={defaultColDef}
rowData={data}
columnDefs={columnDefs}
/>
To refresh a header, you can use the Grid API method refreshHeader.
To refresh a header when the data changes, you can listen to the following Grid Events: onCellValueChanged, onRowDataChanged and onRowDataUpdated.
See this implemented in the following plunkr.
const rowDataChanged = (params) => {
console.log('rowDataChanged');
params.api.refreshHeader();
};
const rowDataUpdated = (params) => {
console.log('rowDataUpdated');
params.api.refreshHeader();
};
const cellValueChanged = (params) => {
console.log('cellValueChanged');
params.api.refreshHeader();
};

When using <AsyncTypeahead/> I can't use onSearch and onInputChange at the same time

So as I said in the title, I can't use onSearch and onInputChange at the same time. If I try to, onSearch gets ignored. Can you tell me why or is there an alternative to onInputChange? Im using AsyncTypeahead in a Form, so I need the values when I click a button.
const [from, setFrom] = useState('');
const onSearchStart = (query) => {
setTypeAhead({
...typeAhead,
startIsLoading: true
})
setFrom(query)
getStations(query).then(r => {
setTypeAhead({
...typeAhead,
startIsLoading: false,
startOptions: r || []
})
})
}
return(
<AsyncTypeahead
id={"from"}
isLoading={typeAhead.startIsLoading}
options={typeAhead.startOptions}
onInputChange={setFrom}
onSearch={onSearchStart}
/>
)
This isn't the whole code, but it shows everything that is causing the error
As a workaround you can use useRef to not trigger the re-render. Here is the code:
const fromRef = useRef('');
const onSearchStart = (query) => {
...
}
return(
<AsyncTypeahead
...
onInputChange={(text) => {
fromRef.current = text;
}}
onSearch={onSearchStart}
/>
)

Redux toolkit slice. Pure functions inside field reducers

Sup ya. Just wanna specify can we use it? I think we can just wanna be sure 100%
https://codesandbox.io/s/redux-toolkit-state-new-array-4sswi?file=/src/redux/slices/slice.js:111-425
const someSlice = createSlice({
name: "someSlice",
initialState: {
users: [],
status: ""
},
reducers: {
actionSuccess: (state, { payload }) => {
state.users = payload.users;
},
actionFailure: (state) => {
// can we use here function?
statusHandler(state)
}
}
});
A reducer should not trigger any side effects. That is, it should not do anything other than change the contents of the state. So you should not call a statusHandler which would trigger external effects.
If your statusHandler function does nothing but update the state, that does seem to work in my testing and I'm not aware of any reason why it shouldn't be okay.
Redux Toolkit uses Immer behind the scenes to deal with immutable updates, so this question basically boils down to whether these two functions, updatedInline and updatedExternally, are equivalent. They are, as far as I can tell.
const {produce} = immer;
const initialState = {
status: ''
};
const updatedInline = produce( initialState, draft => {
draft.status = 'failed';
})
const mutateStatus = (state) => {
state.status = 'failed';
}
const updatedExternally = produce( initialState, mutateStatus )
console.log("Updated Inline: \n", updatedInline);
console.log("Updated Externally: \n", updatedExternally);
<script src="https://cdn.jsdelivr.net/npm/immer#8.0.1/dist/immer.umd.production.min.js"></script>

Mocha MongoDB clean collection before each test

I'm trying to cleanup 2 collections before each test. I'm using mocha --watch to rerun tests while editing the test source files. First run always executes as expected, but consecutive runs gives Topology was destroyed error from mongodb(indicated via result of http request).
I am not really sure why deleteMany deletes my inserted object in consecutive runs.
describe('myCollection1 related tests', () => {
// myCollection1 documents should refer to a valid myCollection2 document.
var foo;
const exampleObject = {name: 'TEST OBJECT', attr1: 'TO'};
beforeEach(() => {
return Promise.all([
mongo.db('mydb').collection('myCollection1').deleteMany({}), // clear collection 1
mongo.db('mydb').collection('myCollection2').deleteMany({}) // clear collection 2
.then(() => mongo.db('mydb').collection('myCollection2').insertOne(exampleObject) // and add a sample object
.then((value) => {
foo = value.ops[0]; // save this as test specific variable so I can use it in my tests.
return Promise.resolve();
})),
]);
});
it('should create a related object', (done) => {
chai.request(server)
.post('/api/v1/foos/')
.send({ related: foo._id })
.then((res) => {
res.should.have.status(200);
res.body.should.be.an('object').with.all.keys('status', 'errors', 'data');
done();
}).catch((err) => {
done(err);
});
});
});
I spotted issue with your promise structure in beforeEach. I'm not sure it is intended or not. I'm afraid it is the culprit. I'm fixing that into below:
beforeEach(() => {
return Promise.all([
mongo.db('mydb').collection('myCollection1').deleteMany({}),
mongo.db('mydb').collection('myCollection2').deleteMany({})
]) // close the promise.all here
.then(() => collections.engines().insertOne(exampleObject)) // close `then` here
.then((value) => {
foo = value.ops[0];
return Promise.resolve();
});
});
Hope it helps

Conditionally populating a path in a Mongo query

I have the following schema:
const connectionSchema = new Schema( {
...
event: {
type: Schema.ObjectId,
ref: 'events'
},
place: {
type: Schema.ObjectId,
ref: 'places'
},
} )
const eventsSchema = new Schema( {
...
place: {
type: Schema.ObjectId,
ref: 'places'
},
} )
When I query for either a single connection or a collection of connections, I want it to check if event is specified and if so return the place from the event, otherwise return the place from the connection.
I'm currently doing something like this when I'm querying a single connection:
let c
const execFunction = ( err, connection ) => {
if ( connection.event ) {
connection.place = connection.event.place
}
c = connection
}
const connectionPromise = Connection.findById( connectionId )
connectionPromise.populate( { path: 'event', populate: { path: 'place' } } )
connectionPromise.populate( 'place' )
connectionPromise.exec( execFunction )
But, I'm trying to avoid having to iterate through the entire collection in the execFunction to do the replace logic on each individual result if I query for a collection instead.
Is there a better (i.e. more performant) way to approach this?
As far as I know, there is no way to do conditional population with mongoose.
In your case, I think you are trying to do "deep population".
Deep population is not supported out of the box, but there is a plugin for that! mongoose-deep-populate
const connectionQuery = Connection.findById( connectionId )
connectionQuery.populate( 'place event event.place' )
connectionQuery.exec( execFunction );
I'm guessing that 'place event.place' will probably do it: 'event' should be implied.
I also expect that, if place or event or event.place are undefined, there won't be an error, they simply won't be populated.
But I don't think there is any way, in one line, to skip populating place when event.place is available.
By the way, if you want to use promises, you could write like this:
return Connection.findById( connectionId )
.populate( 'place event event.place' )
.then( onSuccessFunc );
to propagate errors, or:
Connection.findById( connectionId )
.populate( 'place event event.place' )
.then( onSuccessFunc )
.catch( onErrorFunc );
to handle errors locally. I find that preferable to callbacks, because we don't have to worry about if (err) at every stage.