diff --git a/part6/redux-anecdotes/package-lock.json b/part6/redux-anecdotes/package-lock.json index 05faf47..ee241a6 100644 --- a/part6/redux-anecdotes/package-lock.json +++ b/part6/redux-anecdotes/package-lock.json @@ -8,6 +8,7 @@ "name": "redux-anecdotes", "version": "0.1.0", "dependencies": { + "@reduxjs/toolkit": "^1.9.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -3079,6 +3080,29 @@ } } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -8896,9 +8920,9 @@ } }, "node_modules/immer": { - "version": "9.0.18", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", - "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==", + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -14426,13 +14450,21 @@ } }, "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "dependencies": { "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -14575,6 +14607,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -19238,6 +19275,17 @@ "source-map": "^0.7.3" } }, + "@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "requires": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + } + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -23530,9 +23578,9 @@ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" }, "immer": { - "version": "9.0.18", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", - "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==" + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" }, "import-fresh": { "version": "3.3.0", @@ -27327,13 +27375,19 @@ } }, "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "requires": { "@babel/runtime": "^7.9.2" } }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "requires": {} + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -27445,6 +27499,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", diff --git a/part6/redux-anecdotes/package.json b/part6/redux-anecdotes/package.json index 0278c53..cf2cc47 100644 --- a/part6/redux-anecdotes/package.json +++ b/part6/redux-anecdotes/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@reduxjs/toolkit": "^1.9.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/part6/redux-anecdotes/src/App.js b/part6/redux-anecdotes/src/App.js index 2bc6230..5243334 100644 --- a/part6/redux-anecdotes/src/App.js +++ b/part6/redux-anecdotes/src/App.js @@ -1,12 +1,14 @@ import React from 'react' import List from './components/List' import Form from './components/Form' +import Filter from './components/Filter' const App = () => { return (

Anecdotes

+
diff --git a/part6/redux-anecdotes/src/components/Filter.js b/part6/redux-anecdotes/src/components/Filter.js new file mode 100644 index 0000000..a5dfdba --- /dev/null +++ b/part6/redux-anecdotes/src/components/Filter.js @@ -0,0 +1,23 @@ +import React from "react"; +import { useDispatch } from "react-redux"; +import { setFilter } from "../reducers/filterReducer"; + +const Filter = () => { + const dispatch = useDispatch(); + + const handleChange = (event) => { + dispatch(setFilter(event.target.value)); + }; + + const style = { + marginBottom: 10, + }; + + return ( +
+ Filter +
+ ); +} + +export default Filter; \ No newline at end of file diff --git a/part6/redux-anecdotes/src/components/List.js b/part6/redux-anecdotes/src/components/List.js index a8ae55b..af68f52 100644 --- a/part6/redux-anecdotes/src/components/List.js +++ b/part6/redux-anecdotes/src/components/List.js @@ -4,7 +4,9 @@ import { addVote } from '../reducers/anecdoteReducer' const List = () => { const dispatch = useDispatch(); - const anecdotes = useSelector((state) => state); + const anecdotes = useSelector(({ anecdotes, filter }) => { + return anecdotes.filter((anecdote) => anecdote.content.toLowerCase().includes(filter.toLowerCase())); + }); const handleVote = (id) => { dispatch(addVote(id)); diff --git a/part6/redux-anecdotes/src/index.js b/part6/redux-anecdotes/src/index.js index 5d619bf..16b53e5 100644 --- a/part6/redux-anecdotes/src/index.js +++ b/part6/redux-anecdotes/src/index.js @@ -1,9 +1,15 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import { createStore } from 'redux' +import { createStore, combineReducers } from 'redux' import { Provider } from 'react-redux' import App from './App' -import reducer from './reducers/anecdoteReducer' +import anecdoteReducer from './reducers/anecdoteReducer' +import filterReducer from './reducers/filterReducer' + +const reducer = combineReducers({ + anecdotes: anecdoteReducer, + filter: filterReducer, +}) const store = createStore(reducer) diff --git a/part6/redux-anecdotes/src/reducers/anecdoteReducer.js b/part6/redux-anecdotes/src/reducers/anecdoteReducer.js index 0169b3d..f7d4fe0 100644 --- a/part6/redux-anecdotes/src/reducers/anecdoteReducer.js +++ b/part6/redux-anecdotes/src/reducers/anecdoteReducer.js @@ -37,7 +37,7 @@ export const addAnecdote = (newAnecdote) => { }; }; -const reducer = (state = initialState, action) => { +const anecdoteReducer = (state = initialState, action) => { switch (action.type) { case "VOTE": { const { id } = action.data; @@ -59,4 +59,4 @@ const reducer = (state = initialState, action) => { } }; -export default reducer \ No newline at end of file +export default anecdoteReducer \ No newline at end of file diff --git a/part6/redux-anecdotes/src/reducers/filterReducer.js b/part6/redux-anecdotes/src/reducers/filterReducer.js new file mode 100644 index 0000000..79c4945 --- /dev/null +++ b/part6/redux-anecdotes/src/reducers/filterReducer.js @@ -0,0 +1,17 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const initialState = ""; + +const filterSlice = createSlice({ + name: "filter", + initialState, + reducers: { + setFilter(state, action) { + state = action.payload; + return state; + }, + }, +}); + +export const { setFilter } = filterSlice.actions; +export default filterSlice.reducer; \ No newline at end of file