Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redux Challenges #3

Closed
21 tasks
QuincyLarson opened this issue Jul 1, 2016 · 28 comments
Closed
21 tasks

Redux Challenges #3

QuincyLarson opened this issue Jul 1, 2016 · 28 comments
Assignees

Comments

@QuincyLarson
Copy link
Contributor

QuincyLarson commented Jul 1, 2016

@alayek is in charge of coordinating the creation of these challenges, but he needs your help.

Here's a potential list of challenges:

  • Actions
    • Must have a type
    • Action emitted by user interaction
    • Example of Action payload as a JS object
  • Reducers
    • Using Object.assign()
    • Switch statement to parse relevant action
    • (state, action) => state, must not modify state
    • Reducers can rollback state
  • Store
    • Source of truth for all application state
    • Combine various root level reducers with combineReducer()
    • Access state with getState()
    • Update state with dispatch(action)
    • Register listener via `subscribe(listener)
    • Unregister listener
  • One Way Data Flow
    • Dispatching action with store.dispatch(action)
    • Reducer provides the next state
    • Store combines all root level reducers
    • Store saves a snapshot of application state
  • Advanced section: Async [TODO]

Source: Redux Docs

Thanks @EliBei for helping with finalizing these.

Here are the challenges we have currently planned:

For each challenge, please reply to this GitHub issue with:

  1. Challenge description text
  2. Test suite (using the assert method)
  3. The seed code, which is prepopulated in the editor at the beginning of the challenge
  4. A working solution that makes all tests pass
@alayek
Copy link
Member

alayek commented Aug 7, 2016

There are two video series on YouTube that covers Redux in the context of a real-life app


There is also this list about all things redux.


Would we want to cover asynchronous stores (e.g. redux-thunk) in redux, because usually redux stores takes state and action, and returns state immediately?

@alayek
Copy link
Member

alayek commented Aug 15, 2016

Here's a potential list of challenges:

  • Actions
    • Must have a type
    • Action emitted by user interaction
    • Example of Action payload as a JS object
  • Reducers
    • Using Object.assign()
    • Switch statement to parse relevant action
    • (state, action) => state, must not modify state
    • Reducers can rollback state
  • Store
    • Source of truth for all application state
    • Combine various root level reducers with combineReducer()
    • Access state with getState()
    • Update state with dispatch(action)
    • Register listener via `subscribe(listener)
    • Unregister listener
  • One Way Data Flow
    • Dispatching action with store.dispatch(action)
    • Reducer provides the next state
    • Store combines all root level reducers
    • Store saves a snapshot of application state
  • Advanced section: Async [TODO]

Source: Redux Docs

Thanks @EliBei for helping with finalizing these.

@QuincyLarson
Copy link
Contributor Author

@alayek this looks solid. @BerkeleyTrue has been extremely busy, and is fighting the flu. @BerkeleyTrue what do you think of this list?

@Em-Ant
Copy link
Member

Em-Ant commented Aug 16, 2016

What about generating containers with connect() from React-Redux ?

@alayek
Copy link
Member

alayek commented Aug 16, 2016

@Em-Ant maybe we can cover that in React-Redux?

@BerkeleyTrue
Copy link

@QuincyLarson Looks good.

@Em-Ant Yes that would be covered in react-redux

@QuincyLarson
Copy link
Contributor Author

@alayek have we made any progress on these Redux challenges? As I said on the React thread, we can now use JSX on beta (staging) so you should be able to finish these. @BerkeleyTrue is working on the multi-tab editor, but we should assume it won't be ready in time for launch, and that these will need to be in script within the same single code editor as style and HTML elements.

@QuincyLarson
Copy link
Contributor Author

@alayek your list of topics has @BerkeleyTrue's approval. What else can we do to help get this going. Do you have anyone in mind whom we can delegate this to if you're too busy this week with work (and the React challenges 😄 )?

@alayek
Copy link
Member

alayek commented Sep 6, 2016

@QuincyLarson thanks! I am in touch with elibei and he has been helping me a lot with the challenges. But we might need one more person to help us out with this, if we are to complete and have them production-ready by EOW.

@t3h2mas up for it?

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Learn about Action Types

Actions are payloads of information that send data from your application to your store.

Action type

Actions must have a type property that indicates the type of action being performed. Types should be defined as string constants.

Example action type

Since an action type is a string constant, here is an example action type for an action that would add a todo item to a todo list.

const ADD_TODO = 'ADD_TODO';

Instructions

Make an action type constant. Create a const variable named INCREMENT with the string value of 'INCREMENT'

Challenge Seed

// your code here

Challenge Solution

const INCREMENT = 'INCREMENT';

Challenge Tests


assert(typeof INCREMENT === 'string', 'const INCREMENT must be a string.');
assert(INCREMENT === 'INCREMENT', 'INCREMENT text invalid.');

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Example of a Redux Action Object

Actions are plain JavaScript objects. Actions must have a type property that indicates the type of action being performed.

The example

Imagine we have a Todo List application. An action for the application may be to add a todo item. Let's look at an example redux action for this task.

const ADD_TODO = 'ADD_TODO';

const example = {
    type: 'ADD_TODO',
    text: 'Fold the laundry',
    id: 0

}

Let's create another Action for our todo app.

Instructions

Create an action object that will toggle the completed status of our example todo item.

Start out by making an Action object with the type of TOGGLE_TODO.

Then assign an id property of the action object to match the id of the example action.

Challenge Seed

const TOGGLE_TODO = 'TOGGLE_TODO';

const answer = {

}

Challenge Solution

const TOGGLE_TODO = 'TOGGLE_TODO';

const answer = {
    type: TOGGLE_TODO,
    id: 0
}

Challenge Tests

assert(answer.type === 'TOGGLE_TODO', 'answer.type incorrect');
assert(answer.id === 0, 'answer.id incorrect');

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

@alayek @BerkeleyTrue Can I suggest an Action Creator challenge as well? Action Creators are simple functions that return an action. They are used to dispatch, and later, to connect redux and react.

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Make an Action with an Action Creator

Action creators are functions that create actions. A creator already knows it's action type and is passed any additional information.

Example

Here is an example action creator for our ADD_TODO example.

const ADD_TODO = 'ADD_TODO';

const addTodo = (text) => {
    return {
        type: ADD_TODO,
        text
    };
};

Let's create an Action Creator for our TOGGLE_TODO action.

Instructions

Create a function named toggleTodo that returns an action with the type TOGGLE_TODO and an id.

Challenge Seed

const TOGGLE_TODO = 'TOGGLE_TODO';

// your code here

Challenge Solution

const TOGGLE_TODO = 'TOGGLE_TODO';

const toggleTodo = (id) => {
    return {
        type: TOGGLE_TODO,
        id
    };
};

Challenge Tests

const testAction = toggleTodo(42);
assert(testAction.type === 'TOGGLE_TODO', 'invalid action type');
assert(testAction.id === 42, 'invalid action id');

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Write your first reducer

So far we've talked about Redux Actions. Actions describe that an action accured. They don't describe how the state changes as a result of that action. That is the Reducer's job.

Description

A reducer is a pure function that takes the previous state as well as an action as arguments and returns the new state.

In the simplest form a Reducer can look like

const reducer = (state, action) => {
    return state;
}

At this point a reducer wouldn't be any help. A reducer applies an action to state and returns a new state.
We will explore this by creating a reducer for a counting app.

Example counting app Reducer

App description

Our example app will be able to increment (action) and decrement (action) our count (state) number by 1.

Incrementing the number

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        default:
            return state;
    }
}

A few things to notice...

  1. We have 2 string constant action types declared.
  2. We use pass our action type (you do remember that all actions have a type, right?) to a switch statement.
  3. We return a number. Numbers are immutable. (You do remember that you can't mutate the previous state in your reducer, right?)
  4. We provide a default case that will return our state. There will be more on this later.

Instructions

Amend the reducer to handle DECREMENT action.

Challenge Seed

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        default:
            return state;
    }
}

Challenge Solution

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

Challenge Tests

const oldState = 43;
const action = { type: DECREMENT }
const newState = reducer(oldState, action);
assert(newState === 42, 'the state did not decrement correctly.');

@alayek
Copy link
Member

alayek commented Sep 6, 2016

@t3h2mas thanks!! Yes, please add an action creator challenge where you think would fit best.

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Use the Store to bring it all together

The object that brings it all together. The store is the single source of truth for all application state.

Responsilities of the store

  • holding the state of the application
  • provide access to state with .getState
  • allow updating of the state by .dispatching an action
  • register listeners with .subscribe
  • allow listeners to be unsubscirbed

Creating a store with createStore

function createStore(reducer, [initialState], [enhancer])
parameters:

  • reducer -- the Redux Reducer
  • initialState -- initial state to pass to the store (optional)
  • enhancer -- enhance store with 3rd party utilities/middleware (optional)

returns the store object -- holds the application state. The only way to change the state is by dispatching actions. You can update the ui by subscribing to state changes.

Instructions

Create a Redux Store with createStore. Assign it to a const variable called store. Be sure to pass our reducer function to createStore.

Challenge Seed

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state = 0, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

// your code here

Challenge Solution

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state = 0, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

const store = createStore(reducer);

Challenge Tests

assert(store.getState() === 0, 'store.getState() should equal 0');

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

Dispatch a Redux Action

Applying what we've learned so far.

Actions? Remember those? It's time to put them to use using store.dispatch(ACTION)

What we have so far

In the last challenge we finished with the following code

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const reducer = (state = 0, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

const store = createStore(reducer);

Instructions

Our code has been updated with increment and decrement action creators. Now we can put them to use with our store.

Use store.dispatch to dispatch two INCREMENT actions. Then store the application state to a const named newState. (hint: use getState)

Challenge Seed

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const increment = () => {
    return {
        type: INCREMENT
    };
};

const decrement = () => {
    return {
        type: DECREMENT
    };
};

const reducer = (state = 0, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

const store = createStore(reducer);

Challenge Solution

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const increment = () => {
    return {
        type: INCREMENT
    };
};

const decrement = () => {
    return {
        type: DECREMENT
    };
};

const reducer = (state = 0, action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;

        case DECREMENT:
            return state - 1;

        default:
            return state;
    }
}

const store = createStore(reducer);

store.dispatch(increment());
store.dispatch(increment());
const newState = store.getState();

Challenge Tests

assert(newState === 2, 'state has not incremented correctly. Should be `2`');

@t3h2mas
Copy link

t3h2mas commented Sep 6, 2016

[WIP]

Question: Any suggestions on a test for this one?

Subscribe to an Action

Do something everytime an action is dispatched.

You can subscribe to state changes with the .subscribe method of store.

Example subscription

Log the state to the console everytime an action is dispatched.

store.subscribe(() => {
    console.log(store.getState());
});

Unsubscribing

store.subscribe returns a function that removes the subscribed listener. You can use the returned function by assigning it to a variable.

const unsubscribe = store.subscribe(() => {
    /* ... */
});

// do stuff
unsubscribe();

The challenge

Dispatch at least one INCREMENT action using store.dispatch. Then call the unsubscribe function.

Challenge Seed

const unsubscribe = store.subscribe(() => {
    console.log(store.getState());
});

Challenge Solution

const unsubscribe = store.subscribe(() => {
    console.log(store.getState());
});

store.dispatch({ type: 'INCREMENT' });
unsubscribe();

Challenge Tests

Regex to decide whether the action was dispatched

/store\.dispatch\(\W?{\W?type:\W?\'INCREMENT\'\W?}\W?\);/

and to test for unsubscribe();

/unsubscribe(\(\));?/

@QuincyLarson
Copy link
Contributor Author

@t3h2mas these challenges are off to a great start. I'm going to talk with @BerkeleyTrue about getting a proof of concept working with Enzyme.

In the mean time, keep up the hard work on these.

I updated my original post with a checklist of challenges. If you have a moment, since you know more about Redux than I do, perhaps you can come up with good names for these challenges that conform with our [Verb] [Object] structure (such as "Use an Action Payload as an Object")

My goal is to answer any questions you may have and clear up any ambiguities so you can keep moving forward with designing these challenges.

@Elbei @alayek can also help out on this topic.

@t3h2mas
Copy link

t3h2mas commented Sep 13, 2016

@QuincyLarson the (pure) Redux challenges should be testable w/ any assert testing function. I have tried to update the titles to match the verb object structure more but am open to feedback. My main question is how we can test the subscription and unsubscribe challenge. That one may require a regex or bootstrapped code...

Right now I see the following incomplete from your lists. The rest looks to be covered.

  • Actions are emitted by user interaction -- this one could be introduced with actions but it complicates the simplicity of the Redux api in my opinion. (Ties to React-Redux link)
  • Reducers can roll back state. -- While this is true, it is hard to show from a challenge point of view, and may be better linked to in a continued learning section?
  • Store is the source of truth for all application state -- I can add this to the 'using the store to bring it all together' or 'dispatch a redux action' challenges.

The combining reducers task is an important one but the usefulness behind it may be overly complex until later on in learning/using redux. I will start writing a challenge for it all the same.

@QuincyLarson
Copy link
Contributor Author

@t3h2mas Awesome - I trust your judgement on this. Whatever you think is the best progression. You can ask @alayek @Em-Ant and @BerkeleyTrue if you need a second opinion - they know a lot more about React/Redux than I do :)

@t3h2mas
Copy link

t3h2mas commented Sep 15, 2016

I suggest we move Actions emitted by user interaction as well as combining reducers to the react-redux challenges. Then all that's left is a test for action subscription and adding the source of truth description. I can complete both of those tonight, as well as work on testing for the React challenges. @QuincyLarson @alayek @BerkeleyTrue

UPDATE: If someone can come up with a way to test store subscription, that would be cool.
Otherwise, can someone read through the challenges? I think they're done otherwise.

@QuincyLarson
Copy link
Contributor Author

@t3h2mas That sounds good. I'm glad you're able to write tests for a lot of these. If you can't find a more elegant solution, I recommend going ahead with the dreaded regex tests :)

@t3h2mas
Copy link

t3h2mas commented Sep 16, 2016

Updated with two regexes. Will direct attention towards React tests. @QuincyLarson

@bonham000
Copy link

@ALL Please see the React Issues thread about an update we've made to the development of these React/Redux challenges.

@QuincyLarson
Copy link
Contributor Author

Thanks everyone! I'm closing this thread because our alpha React + Redux challenges are live. Read about them here: https://forum.freecodecamp.com/t/alpha-of-free-code-camps-react-redux-challenges-is-now-live/64492

@t3h2mas
Copy link

t3h2mas commented Dec 8, 2017

@QuincyLarson It saddens me to see that all of the effort and time I spent writing the challenges in this issue seamed to have been tossed aside w/o communication.

@QuincyLarson
Copy link
Contributor Author

@t3h2mas That wasn't our intention here. Would you be interested in helping incorporate some of the challenges you designed into the new React+Redux challenges once we finish importing it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants